Possible Memory Leak?

Hello everybody, I'm using Proclipsing. First of all, do you guys recommend using Proclipsing, if not where can I find the Processing3 jars to attach in my Eclipse project?

The other thing is that I made a little UI system which disposes and resets itself in every Stage Change action. Since every Stage has it's own PImage instances (such as logos, button state images etc), every setup of UI according to that Stage loads up new PImages. I read a lot about memory leak with image() method and PImage in the old versions of Processing. I am not sure if Proclipsing has that leak bug fixed or I'm doing some disposing wrong or even possibly Task Manager measuring it awkwardly wrong. Also I'm not sure which part of my project should I post here in order to clarify that case, since the problem has a weird concept behind it. I'm looking forward to your replies, thanks in advance

Answers

  • edited April 2016

    When we use image(), the PImage is cached in some WeakHashMap:
    http://docs.oracle.com/javase/8/docs/api/java/util/WeakHashMap.html

    So when all references to a PImage is gone, if it's also cached, its entry eventually is deleted as well.

    However, if you wanna avoid the caching completely, use set() or background().

  • edited April 2016

    @GoToLoop I can't use set(), because alpha blending is important in this project. However I made some util class to clear PImage cache, which is still not working as I expect;

        public static void disposePImage(PGraphics pg, PImage img)
        {
            Object cache = pg.getCache(img);
            if (cache instanceof Texture)
                ((Texture) cache).disposeSourceBuffer();
    
            pg.removeCache(img);
        }
    
        public static void disposePImages(PGraphics pg, PImage[] imgs)
        {
            for(PImage img: imgs)
            {
                Object cache = pg.getCache(img);
                if (cache instanceof Texture)
                    ((Texture) cache).disposeSourceBuffer();
                
                pg.removeCache(img);
            }
        }
    

    I will now try to download Processing3.0.2 and attact the library in it in my Eclipse project.

    Edit: I tried if my Proclipsing version had some memory leak problem when I tried that code chunk;

    PGraphics pg;
    
    public void draw()
    {
            pg = createGraphics( width, height );
            pg.beginDraw();
            pg.background(random(255));
            pg.endDraw();
    
            image( pg, 0, 0 );
    
            pg.dispose();
            pg = null;
    }
    

    And the answer is yes, memory usage grew up to 1.8GB less than 1 min

  • After I tried using Processing3.0.2 libraries, the memory leak problem wasn't gone :c Every time I change the Stage (from SettingsStage to MainMenuStage), memory usage keeps growing :c

  • edited April 2016

    It seems like I solved the problem. I overrided image() methods to these;

        @ Override
        public void image(PImage img, float a, float b) 
        {
            super.image(img, a, b);
    
            PGraphics pg = getGraphics();
    
            Object cache = pg.getCache(img);
            if (cache instanceof Texture)
                ((Texture) cache).disposeSourceBuffer();
    
            pg.removeCache(img);
        }
    
        @ Override
        public void image(PImage img, float a, float b, float c, float d) 
        {
            super.image(img, a, b, c, d);
    
            PGraphics pg = getGraphics();
    
            Object cache = pg.getCache(img);
            if (cache instanceof Texture)
                ((Texture) cache).disposeSourceBuffer();
    
            pg.removeCache(img);
        }
    

    I also created a better debug way to see used memory

        private void debug()
        {
            p.fill(0);
            
            float h = p.textAscent()-p.textDescent();
            String fps = String.format("%.0f frames per sec", p.frameRate);
            String ram = String.format("Used Memory: %f GBytes", SystemUtils.usedMemory()/1073741824f);
            
            p.text(fps, p.width-p.textWidth(fps)-10, 10+h);
            p.text(ram, p.width-p.textWidth(ram)-10, 30+h);
        }
    
        /**SystemUtils.java*/
        private static final Runtime runtime = Runtime.getRuntime();
    
        public static long usedMemory()
        {
            return runtime.totalMemory() - runtime.freeMemory();
        }
    
  • edited April 2016 Answer ✓

    Congrats on your workaround! <:-P

    What you're doing is simply removing the WeakHashMap entry right after it's put() there by image(). :ar!

    Even though your workaround solves your issue, your sketch somehow is creating & displaying so many PImage objects per second that the WeakHashMap's caching don't have the time to remove() all of the incoming entries fast enough; and you had to forcibly & prematurely get them outta there! :-&

    Remember that by default, function draw() is called back at about 60 FPS. Even something as simple as:

    public void draw() {
      PGraphics pg = createGraphics(width, height);
      image(pg, 0, 0);
    }
    

    Means 1 PGraphics of width x height cached at about 60 times per second (60 FPS)! :-SS

    Favor pre-creating them within setup(). Then reuse them within draw(). *-:)

    You can clear the whole PGraphics by invoking its clear() or background() methods btW:

    1. https://Processing.org/reference/clear_.html
    2. https://Processing.org/reference/background_.html

    No need to instantiating new 1s all the time... ;;)

  • edited April 2016

    @GoToLoop well UI elements has their setup-a-like methods invoked on Stage change, so PImages are loaded and stored once :P The problem was with the dispose of UI elements on Stage change :D Well also Task Manager was derping and making me get the wrong idea.

    Anyways thanks for answering with that pretty descriptive post! :D

  • edited June 2016

    @GoToLoop I've been following your work (Thank you!) on addressing this memory leak issue and wanted to ask you a few questions if I could and figured on the forum would benefit the most people wrestling with the same issue.

    My dev environment is .. JDK 1.7.0_80 Eclipse Version: 3.8.1 IDE Processing 3.0.1 Linux Mint 17 Cinnamon 64 Bit

    I've made the following sketch, following along with the suggestions here, in the processing IDE to illustrate the needed feature (a rotating background image,) and test if memory consumption starting with the least amounts of variables. I've run this for hours with very little else running OS wise, but from the Cinnamon window manager:

    PGraphics bg;
    PImage image;
    float tick = 0;
    void setup () {
      size (1600, 1200, P3D);
      bg = getGraphics();
    }
    
    void draw() {
      if (image == null) image = loadImage("/home/daniel/workspace/test3/lib/Imgs/9dzign_flame.jpg");
    
      bg.beginDraw();
      bg.clear();
      bg.tint(93,30,93);
      bg.translate(width/2, height/2);
      bg.rotate(tick);
      bg.imageMode(3);
      bg.image(image, 0, 0, width*1.7, height*1.7);
      bg.endDraw();
    
      image(bg, 0, 0);
      tick +=0.01f;
    
    }
    
    @ Override
    public void image(PImage img, float a, float b) 
    {
        super.image(img, a, b);
        PGraphics pg = getGraphics();
        Object cache = pg.getCache(img);
        if (cache instanceof Texture)
            ((Texture) cache).disposeSourceBuffer();
        pg.removeCache(img);
    }
    

    It still leaks very slowly over time but when the same code is used in my java app, it's much much worse, lowering the usability of the application to around 10 minutes before I need to shut it down.

    I've downloaded the source and patched the code to include the subclass additions as subclassing became a rabbit hole of additional classes that needed subclassing in order for everything else to work. patching the code so I could get the workaround to happen gave the same result. It would seem that garbage collection is somehow missing these textures, but apparently reporting they have been cleaned up as the processing sketch alone will exceed it's maximum memory allotment in the preferences.

    My questions are:

    What is the functionality of the WeakHashMap cache as my performance in this case seems fine with out it? Where can I find it in the code so I can place and if gate there to turn the function on and off as that seems like less cycles spend on the issue rather than adding then immediately removing it?

    I was reading in the latest release notes, "Undocumented features (such as the image object in PGraphics) may disappear ..." Does the dev team know what(if anything?) will replace it's functionality?

    If you have any other suggestions or ideas to skin this cat a different way I'm all ears.

    Thank you, GotToLoop and everyone else for your contributions and taking the time to read!!!

  • What is the functionality of the WeakHashMap cache as my performance...

    • Dunno the specifics how PImage WeakHashMap caching helps. Only that image() triggers it.
    • In your case, image() is used both at the main canvas and your bg PGraphics object.
    • AFaIK, each PGraphics got its own WeakHashMap cache. Haven't checked it out though.
    • If it is so, you'd have to removeCache() for both canvas & bg PGraphics instances.

    Where can I find it in the code...

    https://GitHub.com/processing/processing/tree/master/core/src/processing

    Undocumented features (such as the image object in PGraphics) may disappear...

    No idea what that means. Hope it's not about removing PImage as PGraphics' parent class.

    If you have any other suggestions or ideas to skin this cat a different way I'm all ears.

    The ideal approach is load all resources the sketch is gonna need before draw() starts. *-:)

Sign In or Register to comment.