PImage garbage not being properly collected

Howdy. I’m making a display intended to be constantly running on a TV as a sort of information Plaque.
I utilize PImage objects as I have a folder of pictures I wish to display as a slideshow, and I am aware that those objects tend to take up a lot of memory.

My program “resets” at midnight every day, meaning it scraps all of the elements I have created and creates new ones, along with fetching the necessary information from online etc. All of it works fine except my array of PImages. The previously used images, even when overwritten and attempted to be collected by the almighty GC, persist in memory and clog up my RAM. After even two or three resets, I exceed my allocated memory (2 whole-a** GBs), and I get a heap overflow exception.

I have researched quite a bit, tried to call every collection “suggester” (g.removeCache(PImage), System.gc(), and setting the objects to NULL), but the issue persists.
Is there a fix for this currently? Am I making a terrible rookie mistake? Any help is immensely appreciated :slight_smile:

Here’s the project if that would aid in your suggestions https://github.com/nossonCotlar/Plaque

1 Like

This seems to run stable without “running out of memory”:

  1. In Processing “Preferences” I set maximum available memory to 2048 MB.

  2. keyPressed() modified so it would do the same as your midnight “reset” for testing.

  3. I added a “flag” to keyPressed() so it waited 10 secs before next key press.; otherwise it will fill memory.

  4. Also added a timer to “reset” every 15 secs. I will see how long it runs and update later…

I used VisualVM 1.4.3 to monitor heap size and used heap.
image

Code only showing what was modified:

boolean stop = false;
boolean flag = false;
long time;
long timeWait = 10000;

void setup() {
// Same
}

void draw() {
//Same

  updateCheck();

  if (time+timeWait <= millis()) flag = true;
  println(flag);

// timer
  if (time+15000 <= millis())
    {
    freeStuffUp();
    thread("init");
    time = millis();
    flag = false;
    }

  println(mouseX, ' ', mouseY);
  //println(frameRate);
  
  if (time+timeWait <= millis()) flag = true;
  println(flag);
//  System.gc();
  }

void keyPressed() {
  if (key == 'p') saveFrame("save.png"); 
  if (key == 'r' && flag == true) {
    frameCount = -1;
    freeStuffUp();
    flag = false;
    time = millis();
    thread("init");
  }
}

I had similar issues in the past and wanted to see what I could discover.
I did not spend too much time on this…

2 Likes

After 15 minutes and resetting every 15 secs:

Thank you for sharing the code!
I learned a lot going through it.

:slight_smile:

Another exploration with less memory:

  1. In Processing “Preferences” I set maximum available memory to 1024 MB.
  2. keyPressed() modified so it would do the same as your midnight “reset” for testing.

It crashed on a key press:

void keyPressed() {
  if (key == 'p') saveFrame("save.png"); 
  if (key == 'r') {
    frameCount = -1;
    freeStuffUp();
    thread("init"); \\ Added this!
  }

You can see where it crashed at end of image:

Same settings as above but with the addition of System.gc() at end of draw():

  println(mouseX, ' ', mouseY);
  //println(frameRate);

  System.gc();
}

And voila! It would only crash if I pressed it during a peak cycle of the “used heap” otherwise seemed to run fine; I would use more memory!

:slight_smile:

2 Likes

Thanks for your time @glv!! This really helps a lot, I will implement your changes. And I’m glad you learned from the code haha no worries :slight_smile: Take care

Probably if you’re loading all images at once! You’d be better using requestImage() with possibly two PImage fields (current and next) to load the images on demand.

If you ever need System.gc() in finished code you’re almost certainly doing something wrong.

I had thought about that early on, seemed like the most sensible way to minimize memory usage, but that proved to be quite processor intensive throughout runtime, not just in setup() and init(), where the heavy functions would usually be executed.
That being said, I believe that might be the best way to go about things, so I’ll try my hand at your method as well. Cheers for the suggestion <3

–Edit: I may have spoken too soon because I had never used requestImage() instead of loadImage() :smiley: seems like a much better option for this setting and it negates my concern

1 Like

I had related issues a year ago with a growing heap:

And this what it does over time until it crashes:

Please share if you learn anything new.

:slight_smile:

As you’re already using VisualVM, if you haven’t already, use the heap dump option under Sampler / Memory; find instances that shouldn’t still be there, and right-click find GC root. That’ll tell you what’s keeping hold of references.

Memory issues can also be caused by native memory, although usually only with the OpenGL renderer or other native library - that memory is free’d by the garbage collector, but as it’s not part of the Java heap it doesn’t trigger garbage collection.

2 Likes