![](/i/fill.gif) |
![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Before running: 6.8 meg of unshared memory used.
While running: 7.1 meg of unshared memory used.
Doesn't look like GC tends to expand indefinitely, even when you *do* have
tons of memory available. If you use allocations bigger than about 80K, it
works even better, because .NET tracks them separately in a separate
generation.
(Patched into a larger program just to test it out, so it's not 6 meg for
this simple bit of code.)
private void button6_Click(object sender, EventArgs e)
{
byte[] outer; byte[] outer2;
for (int i = 0; i < 1024 * 1024 * 200; i++)
{
outer = new byte[8192];
byte[] inner1 = new byte[8192];
byte[] inner2 = new byte[50190];
for (int j = 0; j < 1024; j++)
{
outer2 = new byte[30000];
}
}
}
--
Darren New, San Diego CA, USA (PST)
"Ouch ouch ouch!"
"What's wrong? Noodles too hot?"
"No, I have Chopstick Tunnel Syndrome."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> private void button6_Click(object sender, EventArgs e)
> {
> byte[] outer; byte[] outer2;
> for (int i = 0; i < 1024 * 1024 * 200; i++)
> {
> outer = new byte[8192];
> byte[] inner1 = new byte[8192];
> byte[] inner2 = new byte[50190];
> for (int j = 0; j < 1024; j++)
> {
> outer2 = new byte[30000];
> }
> }
> }
Are you sure the compiler is not performing some clever optimization
tricks there like: "Hmm, this code allocates a piece of memory to this
pointer, the pointer demonstrably is not passed to anything anywhere
but is re-assigned a new memory block, thus the allocated memory is
likewise freed at that point, so I'll put a GC flag to this block of
memory to tell the GC engine that it's free."
I wonder if it makes any difference if the pointer is passed to some
function which the compiler doesn't know what it does at this point.
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Are you sure the compiler is not performing some clever optimization
> tricks there like: "Hmm, this code allocates a piece of memory to this
> pointer, the pointer demonstrably is not passed to anything anywhere
> but is re-assigned a new memory block, thus the allocated memory is
> likewise freed at that point, so I'll put a GC flag to this block of
> memory to tell the GC engine that it's free."
I'm not sure that's the case. I'm almost certain that it doesn't reuse
memory in the middle of the block that's already been freed.
> I wonder if it makes any difference if the pointer is passed to some
> function which the compiler doesn't know what it does at this point.
I'll give that a go and let you know. I'll try to make it more convoluted.
--
Darren New, San Diego CA, USA (PST)
"Ouch ouch ouch!"
"What's wrong? Noodles too hot?"
"No, I have Chopstick Tunnel Syndrome."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New wrote:
> I'll give that a go and let you know. I'll try to make it more convoluted.
OK. Now when it starts, it's about 6.9 meg. As you move the mouse around the
UI and it highlights different stuff and pops up tooltips and such, it
drifts up to about 7.2 or 7.4 meg. If you start the code here, it pops up
to 10.25 meg and stays there. When the routine returns, it holds about 10.20
meg, so it isn't doing a GC at the end. It probably schedules a GC after so
many allocations or so many function entries or something; that's usually
how these things are set up. If it does a GC and finds it doesn't reap
much, it turns down how often the GC runs.
I compiled these as separate projects, so they're only combined at the point
where you actually run them (i.e., dynamically linked), so I'm pretty sure
there's no unexpected optimizations going on. (Honestly, I can't even figure
out how much garbage there is after each cycle, so ... ;-)
private void button6_Click(object sender, EventArgs e)
{
byte[] outer; byte[] outer2;
Fozz f; Fozz g;
for (int i = 0; i < 1024 * 1024 * 200; i++)
{
outer = new byte[8192];
byte[] inner1 = new byte[8192];
byte[] inner2 = new byte[50190];
f = new Fozz(inner2);
for (int j = 0; j < 1024; j++)
{
outer2 = new byte[30000];
g = new Fozz(f);
f = new Fozz(outer2);
outer2 = g.thingie(2);
f = new Fozz(g.thingie(1));
}
}
}
Separate assembly:
public class Fozz
{
byte[] save;
byte[] also;
public Fozz(byte[] incoming)
{
save = incoming;
also = (byte[])incoming.Clone();
}
public Fozz(Fozz f)
{
save = (byte[])f.save.Clone();
also = (byte[])f.also.Clone();
}
public byte[] thingie(int x)
{
if (x == 1) return (byte[])save.Clone(); else return also;
}
}
--
Darren New, San Diego CA, USA (PST)
"Ouch ouch ouch!"
"What's wrong? Noodles too hot?"
"No, I have Chopstick Tunnel Syndrome."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New wrote:
> I compiled these as separate projects, so they're only combined at the
> point where you actually run them (i.e., dynamically linked), so I'm
> pretty sure there's no unexpected optimizations going on. (Honestly, I
> can't even figure out how much garbage there is after each cycle, so ...
> ;-)
But .NET, like Java, optimizes at runtime, after linking everything
together....
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Nicolas Alvarez wrote:
> But .NET, like Java, optimizes at runtime, after linking everything
> together....
Possibly, yes. I don't know it does the sort of optimization we were talking
about, which was basically escape analysis between independent assemblies.
If it does, that just goes to show the point more - you don't have to use up
all that much memory.
The naive view of garbage collection is that you run the GC when you run out
of memory, and hence you tend to take a lot of memory away from other
processes. This was the complaint Warp expressed at one point that I felt
like investigating. Apparently the GC in .NET runs even when you're nowhere
near running out of memory, which makes sense in all kinds of ways. And
there are probably pretty simple ways to dynamically tune the GC frequency
as the program runs to keep things pretty well trimmed without too much GC
overhead. Remember that the more often you GC, the less time each one takes.
--
Darren New, San Diego CA, USA (PST)
"Ouch ouch ouch!"
"What's wrong? Noodles too hot?"
"No, I have Chopstick Tunnel Syndrome."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |