0

I'm a bit of a noob with C#. I've tried to do the right thing in terms of managing memory, but I am now getting "out of memory" errors.

UPDATE #2 5 DEC

I solved it! (sort of) I don't understand why, but when I just run the code from my test app with hard-coded parameters, it worked fine, but when I use a dialog (based on OokiDialog) to get some parameters, that's when the memory doesn't get free up.

So, there's something about the dialog that seems to make it want to hold onto these objects, even though they have nothing to do with the dialog itself.

In the end, I was able to "fix" the problem by running the code in it's own task:

Task taskA = Task.Run(() => MakeFiles(path, name));
taskA.Wait();

It gets me working again, but still, I'd LOVE some theories on WHY this is so?

UPDATE 5 DEC

After continued struggles, I decided to copy the relevant code to a test project where I could experiment. After copying, I ran it, and was stunned to see that the memory went up and then DOWN on each iteration. No more "out of memory"!

Still trying to figure out how/why.

UPDATE 4 DEC

I've ruled out the GIF encoder. I commented out that part of the code and the memory still went up and up.

I'm doing quite a bit of loading images and copying their pixels to byte[] arrays. I've been pretty careful to use local variables for the most part, so they shouldn't be persisting.

Is there any way to tell what object or at least class of object is creating those "StreamAsIStream [Strong Handle]" references? ORIGINAL QUESTION

I've done some profiling of the heap and can see that the main culprit is MemoryStreams.

I use MemoryStream in a couple of places, but have been careful to always use "using":

using (MemoryStream mem = new MemoryStream())
{
    ...
}

I noticed that most of the streams on the heap end up showing as:

StreamAsIStream [Strong Handle]

That "strong handle" seems like a clue, perhaps? I.e. because it's a strong handle the GC won't clean it up?

I've tried explicitly disposing the streams and then forcing garbage collections, but no matter what I do, they just seem to stick around and eventually I get memory errors.

Could it be something else is internally using a MemoryStream and I'm looking in the wrong place?

Any thoughts or other things I can look at?

xtempore
  • 5,260
  • 4
  • 36
  • 43
  • `StreamAsIStream` seems to be an internal class used in`System.Windows.Media` and nothing to do with `MemoryStream` – TheGeneral Dec 03 '20 at 06:26
  • Have a look at the distribution of free space in the heap. A common cause of these problems is that there's a native pointer to some data on the managed heap, and it prevents the heap from being compacted (except for LOH, .NET always allocates on top of the heap). – Luaan Dec 03 '20 at 06:40
  • @TheGeneral I think you're onto something. A bit more prodding and it looks like it's possibly where I use a GifBitmapEncoder. Still struggling to see what I'm doing wrong, but I'll update the question. – xtempore Dec 03 '20 at 06:43
  • @Luaan I'm not using any native pointers in my code as far as I know. Is there something else that might be? E.g. GifBitmapEncoder. – xtempore Dec 03 '20 at 06:49
  • Does `GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect();` help? – mjwills Dec 03 '20 at 07:22
  • @mjwills Doesn't seem to make any difference. – xtempore Dec 03 '20 at 08:17
  • It doesn't have to be _your_ code. Anything that works with native code (`GifBitmapEncoder` probably is) or I/O (usually networking) is suspect. The main thing to check is how fragmented your CLR heap is. CLRProfiler is quite enough for that. – Luaan Dec 03 '20 at 09:50
  • I don't know if anyone is still following this, but if so please see the latest update above. – xtempore Dec 04 '20 at 21:33
  • `and then forcing garbage collections` Please show _ how_ you are doing that. – mjwills Dec 05 '20 at 07:54
  • I don't think garbage collection is the issue. I can see in the diagnostic tools that GC is being attempted. The difference between being called in the dialog or from a task is that in the dialog GC doesn't do anything but from the task it does. I don't really know why though. – xtempore Dec 06 '20 at 02:57

1 Answers1

0

You can call stream.Close(); to free up the memory used by the object. The using statement should handle it for you, but I have seen the occasional instance where garbage collection doesn't recognize that the object is no longer in use, especially if you happen to be using WPF.

You could also try implementing the IDisposable interface and then force disposal of objects as well as force garbage collection. I've linked Microsoft doc for that process here.

Here is a more detailed description of Dispose() vs Close() as well.

Wellerman
  • 846
  • 4
  • 14
  • `You can call stream.Close(); to free up the memory used by the object.` That does literally nothing (in terms of freeing memory) for a `MemoryStream`. https://referencesource.microsoft.com/#mscorlib/system/io/memorystream.cs – mjwills Dec 03 '20 at 07:21
  • I already close the stream. It's just happening in a higher procedure, that calls this one several times. I've implemented IDisposable on a couple of my objects but it didn't help, and I can now see they weren't the ones causing the problem. – xtempore Dec 03 '20 at 08:13