none
Garbage collection RRS feed

  • Question

  • Hi,

    I've cache a bunch of images in a startup screen (like a splash screen) as follows:

    m_cache.abmpMachines = new Bitmap[CommonDefinitions.NUM_MACHINE_IMAGES];

    string szImage = "App.Resources.Machines.Machine1.jpg";
    Assembly _assembly = Assembly.GetExecutingAssembly();
    Stream file = _assembly.GetManifestResourceStream(szImage);
    m_cache.abmpMachines[CommonDefinitions.MACHINE_1] = new Bitmap(file);

    szImage = "LomaOPC.Resources.Machines.Machine2.jpg";
    file = _assembly.GetManifestResourceStream(szImage);
    m_cache.abmpMachines[CommonDefinitions.MACHINE_2] = new Bitmap(file);

    // etc

    I then display these in a PicTureBox while the startup process (checking registry, initialising network connections) goes on. Once all startup actions are complete, I overlay a new screen which becomes the main run screen (and the startup screen gets hidden). I'd like the cache of images to be deleted, but there is no "delete" command like in C++.

    How do I achieve this? The cache is consuming around 100MB! I thought it would be automatically collected after a few seconds, but the memory is never returned until I completely close the app.

    Thanks,
    Alain

    • Moved by Leo Liu - MSFT Monday, February 6, 2012 6:21 AM Moved for better support. (From:Visual C# Language)
    Friday, February 3, 2012 4:32 PM

Answers

  • The cache is consuming 100MB which is native memory. But, on clr heap only handles are present where each handle is of 32 bits on 32 bit machines. So, if 10 images result in 100Mb native memory usage, only 40 bytes occupy in application's heap which may not be sufficient to trigger a garbage collection.

    Hence, I suggest you to use GC.AddMemoryPressure and GC.RemoveMemoryPressure methods to overcome this problem. Here, instead of directly creating a Bitmap, you create a class that holds a bitmap as below,

    Class BitmapImage
    {
         Bitmap image;
         public Bitmap GetImage()
         {
             return image;
         }
    
         public BitmapImage(Stream stream)
         {
              if(bmp != null)
              {
                   image = new Bitmap(stream);
                   GC.AddMemoryPressure(2 * 1024 * 1024);
              }
         }
      
         ~BitmapImage()
         {
              if(image != null)
              {
                   image.Dispose();
                   GC.RemoveMemoryPressure(2 * 1024 * 1024);
              }
         }
    }

    Here, I assume each bitmap consumes 2MB of native memory. But, each occupies just 4 bytes of managed heap. So, whenver you create an object of above class, it add's a memory pressure of 2MB which means CLR assumes the object consumes 2MB. So, this makes GC to happen frequently and hence images will be disposed. You can use this class as,

    m_cache.abmpMachines = new Bitmap[CommonDefinitions.NUM_MACHINE_IMAGES];
    
    string szImage = "App.Resources.Machines.Machine1.jpg";
    Assembly _assembly = Assembly.GetExecutingAssembly();
    Stream file = _assembly.GetManifestResourceStream(szImage);
    m_cache.abmpMachines[CommonDefinitions.MACHINE_1] = new BitmapImage(file).GetImage();
    
    szImage = "LomaOPC.Resources.Machines.Machine2.jpg";
    file = _assembly.GetManifestResourceStream(szImage);
    m_cache.abmpMachines[CommonDefinitions.MACHINE_2] = new BitmapImage(file).GetImage();

    You can find more on this at below blog,

    http://blogs.msdn.com/b/jmeier/archive/2006/10/09/performance-guideline_3a00_-use-addmemorypressure-while-consuming-unmanaged-objects-through-com-interop.aspx

    I hope this helps.


    Please mark this post as answer if it solved your problem. Happy Programming!

    Sunday, February 12, 2012 6:08 AM
  • If you want the memory to be paged out, try setting the working set for the process - see if that helps:

    http://msdn.microsoft.com/en-us/library/windows/desktop/ms686234(v=vs.85).aspx 

    And as mentioned, garbage collection will de-allocate only what is not referenced. There is also the issue that the NET managed heap is a pile of memory that's part of the total memory in use by the process. If the managed heap is full at 100MB and you free enough that only 50MB is being used, it's by no means clear that the managed heap will be shrunk in response to that.  It's likely that the managed heap will remain at 100MB because you don't really want that memory being continually deallocated and then reallocated. The result is that the memory in use by your process won't go down just because some managed memory was freed.

    It's also worth mentioning again that if you want to get rid of your bitmaps, call their Dispose() methods.


    Phil Wilson

    Monday, February 13, 2012 9:59 PM

All replies

  • There's no "delete" because the garbage collector collects things automatically. Part of the "automatically" aspect is that the GC decides on its own when to run. If you have plenty of memory to spare, it won't bother running. I'm guessing you have a lot more than 100MB of memory on your machine, so unless you actually run into a OutOfMemoryException at some point, don't worry about it. If the CLR needs some of the 100MB back, it will clear it out. Otherwise it won't take up CPU resources by performing collection.

    For more info, see


    jmh
    Friday, February 3, 2012 4:53 PM
  • Thanks. You're right that I have plenty more than 100MB on my development machine. But I don't like the non-determanism that GC uses: "I will run when I want, at some indeterminate time in the future...so there!".

    Is there nothing I can do to force the memory to be freed up? I will read those links, BTW, just a quick reply...

    Thanks,
    Alain

    <jmh_gr> wrote in message news:8b452241-c364-4c1e-a4cd-77961976dcd4@communitybridge.codeplex.com...

    There's no "delete" because the garbage collector collects things automatically. Part of the "automatically" aspect is that the GC decides on its own when to run. If you have plenty of memory to spare, it won't bother running. I'm guessing you have a lot more than 100MB of memory on your machine, so unless you actually run into a OutOfMemoryException at some point, don't worry about it. If the CLR/needs/ some of the 100MB back, it will clear it out. Otherwise it won't take up CPU resources by performing collection.

    For more info, see

    * Garbage Collection <http://msdn.microsoft.com/en-us/library/0xy59wtx.aspx>*(extensive explanation)?*
    * Best Practice for Forcing Garbage Collection in C# <http://stackoverflow.com/questions/233596/best-practice-for-forcing-garbage-collection-in-c-sharp>


    jmh

    Friday, February 3, 2012 5:08 PM
  • hi,

    Try

    USING(Object myobj= new Object())

    {

    /// your code

    }

    Rrather than calling garbage collector. You object will get automatically destroyed once your scope is over.


    java
    Friday, February 3, 2012 5:15 PM
  • Is there nothing I can do to force the memory to be freed up?

    You can always call GC.Collect(). I just didn't mention it at first because it is almost always discouraged.


    jmh
    Friday, February 3, 2012 5:43 PM
  • You can unload appdomains at will.

    http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/3d072d04-c2e8-4dd6-ac2c-e9e20adb0fb4

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://thesharpercoder.blogspot.com/

    Saturday, February 4, 2012 12:47 AM
  • But the memory won't necessarily be reclaimed - that only happens when GC runs.

    Regards David R
    ---------------------------------------------------------------
    Object-oriented programming offers a sustainable way to write spaghetti code. - Paul Graham.
    Every program eventually becomes rococo, and then rubble. - Alan Perlis
    The only valid measurement of code quality: WTFs/minute.
    Saturday, February 4, 2012 12:18 PM
  • Hi Alain,

    If you have a very specific memory consumption goal (like 50MB max) I suggest you wirte a unmanaged C++ app that will take care of image loading and unloading.

    Since it looks like you are building desktop app, I would suggest that you leave memory management to .NET GC.

    As long as you Dispose your cache resources, you should be good.
    CLR Profiler can help you find memory leaks, if any.

    Best!


    Martin Kulov
    www.kulov.net

    Microsoft Regional Director
    VS ALM MVP, MCT, MCSD, MCPD, INETA Speaker

    Friday, February 10, 2012 11:07 PM
  • The cache is consuming 100MB which is native memory. But, on clr heap only handles are present where each handle is of 32 bits on 32 bit machines. So, if 10 images result in 100Mb native memory usage, only 40 bytes occupy in application's heap which may not be sufficient to trigger a garbage collection.

    Hence, I suggest you to use GC.AddMemoryPressure and GC.RemoveMemoryPressure methods to overcome this problem. Here, instead of directly creating a Bitmap, you create a class that holds a bitmap as below,

    Class BitmapImage
    {
         Bitmap image;
         public Bitmap GetImage()
         {
             return image;
         }
    
         public BitmapImage(Stream stream)
         {
              if(bmp != null)
              {
                   image = new Bitmap(stream);
                   GC.AddMemoryPressure(2 * 1024 * 1024);
              }
         }
      
         ~BitmapImage()
         {
              if(image != null)
              {
                   image.Dispose();
                   GC.RemoveMemoryPressure(2 * 1024 * 1024);
              }
         }
    }

    Here, I assume each bitmap consumes 2MB of native memory. But, each occupies just 4 bytes of managed heap. So, whenver you create an object of above class, it add's a memory pressure of 2MB which means CLR assumes the object consumes 2MB. So, this makes GC to happen frequently and hence images will be disposed. You can use this class as,

    m_cache.abmpMachines = new Bitmap[CommonDefinitions.NUM_MACHINE_IMAGES];
    
    string szImage = "App.Resources.Machines.Machine1.jpg";
    Assembly _assembly = Assembly.GetExecutingAssembly();
    Stream file = _assembly.GetManifestResourceStream(szImage);
    m_cache.abmpMachines[CommonDefinitions.MACHINE_1] = new BitmapImage(file).GetImage();
    
    szImage = "LomaOPC.Resources.Machines.Machine2.jpg";
    file = _assembly.GetManifestResourceStream(szImage);
    m_cache.abmpMachines[CommonDefinitions.MACHINE_2] = new BitmapImage(file).GetImage();

    You can find more on this at below blog,

    http://blogs.msdn.com/b/jmeier/archive/2006/10/09/performance-guideline_3a00_-use-addmemorypressure-while-consuming-unmanaged-objects-through-com-interop.aspx

    I hope this helps.


    Please mark this post as answer if it solved your problem. Happy Programming!

    Sunday, February 12, 2012 6:08 AM
  • If you want the memory to be paged out, try setting the working set for the process - see if that helps:

    http://msdn.microsoft.com/en-us/library/windows/desktop/ms686234(v=vs.85).aspx 

    And as mentioned, garbage collection will de-allocate only what is not referenced. There is also the issue that the NET managed heap is a pile of memory that's part of the total memory in use by the process. If the managed heap is full at 100MB and you free enough that only 50MB is being used, it's by no means clear that the managed heap will be shrunk in response to that.  It's likely that the managed heap will remain at 100MB because you don't really want that memory being continually deallocated and then reallocated. The result is that the memory in use by your process won't go down just because some managed memory was freed.

    It's also worth mentioning again that if you want to get rid of your bitmaps, call their Dispose() methods.


    Phil Wilson

    Monday, February 13, 2012 9:59 PM
  • Are you clearing your cache once your main page has loaded? It's possible that something is still referring to the objects and so the GC isn't clearing them up. It's also possible that the GC has cleaned up the memory, but hasn't returned it to the OS because the OS hasn't put memory pressure on the process yet, so the runtime figures it would be more work to return the memory if it's just going to ask for more memory a few seconds later.

    If you take a crash dump of your process well after you think that the splash screens bitmaps should have been cleaned up, you can do a search for the Bitmap type in Windbg, and look at the different Bitmap references. Then call !gcRoot on the different instances and if they go through your cache, then you'll know that they're not getting cleaned up because the cache is still referring to them.

    Tuesday, February 14, 2012 12:06 AM