Creating Image instance from byte[] - Am I leaking memory or not?

S

stevek

While researching a GDI+ bug I had Task Manager up to see what was happening
with my memory. I was very surprised to see that my application was
consuming a TON of memory and each time I repeated one specific task it
would grab another 9-10 Mb. I narrowed this down to a single method that
seems to be leaking memory but I don't see why it is. FYI, the process I'm
watching in TaskMgr is myapplication.vshost.exe

Here is the method that is eating up memory:
public static Image ImageFromBytes(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data, 0, data.Length))
{
ms.Write(data, 0, data.Length);
return Image.FromStream(ms, true);
}
}


Simple, right? Well as soon as this method returns (which due to
IDisposable is closing and disposing my stream) the memory jumps up 9-10 Mb.
I don't get it, I don't see the problem. The size of the source byte[] is
around 120Kb and I would imagine the deserialized Image instance is about
the same.

I have also tested this outside of the debugger and am seeing the same
behavior.
Anyone have any ideas for me? Is there a bug in the above method??

-Steve
 
P

Peter Duniho

[...]
Here is the method that is eating up memory:
public static Image ImageFromBytes(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data, 0, data.Length))
{
ms.Write(data, 0, data.Length);
return Image.FromStream(ms, true);
}
}


Simple, right? Well as soon as this method returns (which due to
IDisposable is closing and disposing my stream) the memory jumps up 9-10
Mb.

MemoryStream.Dispose() does practically nothing. And if your byte[] is
only 120KB, that wouldn't account for a 10MB increase anyway.
I don't get it, I don't see the problem. The size of the source byte[]
is
around 120Kb and I would imagine the deserialized Image instance is about
the same.

Why would you imagine the deserialized Image instance is about the same?
What format is the image? What are its pixel dimensions? Most image
files these days are compressed JPEG, PNG, TIFF, etc. And a compressed
image can get a lot larger when it's deserialized.

Unfortunately, you didn't post a concise-but-complete code example. But
assuming you are retaining each Image instance returned by that method, I
would not be surprised at all to see your memory usage rising
monotonically. Even if you don't retain each instance, if you're not
explicitly disposing them memory usage could rise for some time before the
instances start getting finalized and disposed on your behalf.

Finally note that TaskManager provides a very poor view into memory usage;
..NET is a layer between your application and the OS that obscures what
your own application's memory usage really is. Even once your application
has released memory, .NET may hang on to it for some time rather than
releasing it to the OS immediately. That may or may not be related to
your specific observation, but it's something to be aware of.

Pete
 
S

stevek

Peter Duniho said:
MemoryStream.Dispose() does practically nothing. And if your byte[] is
only 120KB, that wouldn't account for a 10MB increase anyway.

Since posting this question I was doing some additional research and came to
the same conclusion that Dispose() would have no effect and apparently the
memory is still available for reading after calling Dispose.
I don't get it, I don't see the problem. The size of the source byte[]
is
around 120Kb and I would imagine the deserialized Image instance is about
the same.

Why would you imagine the deserialized Image instance is about the same?
What format is the image? What are its pixel dimensions? Most image
files these days are compressed JPEG, PNG, TIFF, etc. And a compressed
image can get a lot larger when it's deserialized.

Well maybe not about the same, but certainly not 10Mb. The image is a 1bit
GIF, 800x1200 @ 200DPI (a shipping label). I saved the Image to disk and
it's about 90Kb on disk.

[...]
Finally note that TaskManager provides a very poor view into memory usage;
.NET is a layer between your application and the OS that obscures what
your own application's memory usage really is. Even once your application
has released memory, .NET may hang on to it for some time rather than
releasing it to the OS immediately. That may or may not be related to
your specific observation, but it's something to be aware of.

This makes sense and I knew it wasn't very accurate, but I figured for
ballpark estimations it was acceptable. Do you have a utility that you
would recommend for more accurate details when debugging? I'm always happy
to learn about better tools.
Now that I have supplied the additional information about the image I'm
working with, do you still think that 10Mb is typical for an Image instance?
 
S

stevek

Well maybe not about the same, but certainly not 10Mb. The image is a
1bit GIF, 800x1200 @ 200DPI (a shipping label). I saved the Image to disk
and it's about 90Kb on disk.

My assumption that a deserialized Image instance would "not be that big" was
very incorrect! I just conducted a small test to see if there was a
difference from loading an image from disk straight to an Image instance
versus going to a byte[] first then using a MemoryStream. They are within
24Kb of each other. I also noticed that Dispose() seems to free the mempry
right away so I will review my main application and check to see where I'm
holding onto these references and where I can Dispose them correctly.

Thanks for responding to my earlier post, at least now I know there isn't a
problem with my MemoryStream code.

-Steve
 
N

not_a_commie

public static Image ImageFromBytes(byte[] data)
{
    using (MemoryStream ms = new MemoryStream(data, 0, data.Length))
    {
        ms.Write(data, 0, data.Length);
        return Image.FromStream(ms, true);
    }
}

Um, what is that Write call in there for? Didn't you mean this?

public static Image ImageFromBytes(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data))
return Image.FromStream(ms, true);
}

And are you getting rid of your byte[] reference in the calling
function?
 
P

Peter Duniho

My assumption that a deserialized Image instance would "not be that big"
was
very incorrect!

Indeed. Assuming internally the image winds up a 32bpp image, you've got
nearly 1 million pixels, making for about 3.5-4MB in-memory. Assuming a
second copy of the data is made as part of the deserialization process,
that gets you a lot closer to your "9-10 Mb" figure.

Without a memory profiler, it's impossible to know for certain where
memory is being used, but I'd say that the numbers seem to at least be "in
the ballpark".
I just conducted a small test to see if there was a
difference from loading an image from disk straight to an Image instance
versus going to a byte[] first then using a MemoryStream. They are
within
24Kb of each other. I also noticed that Dispose() seems to free the
mempry
right away so I will review my main application and check to see where
I'm
holding onto these references and where I can Dispose them correctly.
[...]

Good idea. :)

Pete
 
H

Hans Kesting

stevek presented the following explanation :
Here is the method that is eating up memory:
public static Image ImageFromBytes(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data, 0, data.Length))
{
ms.Write(data, 0, data.Length);
return Image.FromStream(ms, true);
}
}
In addition to what the others said, you are disposing the MemoryStream
here (through the "using"). But what about the original byte[]? That
will not disappear when you dispose of the MemoryStream.

(and then there is the Image you created)

Hans Kesting
 
J

Jeff Johnson

The image is a 1bit GIF, 800x1200 @ 200DPI (a shipping label). I saved
the Image to disk and it's about 90Kb on disk.

Which says NOTHING about its in-memory representation. Unless it's saved as
a .BMP, more than likely some form of compression is in effect.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top