0

I'm currently working on optimizing my application and I noticed that it takes up nearly 1 gig of RAM. I did some profiling and found the problem: I was allocating a lot of memory by creating int arrays holding pixel data in a texture class I made. But this confuses me, because when creating a new array the old array is not used anymore and there is no reference to it anywhere.

I've written a test program that reproduces this problem:

public class Main {

    static class ArrayHolder {
        int[] array;

        void init() {
            array = null;
            array = new int[4000];
        }
    }

    public static void main(String[] args) {
        ArrayHolder holder = new ArrayHolder();
        for (int i = 0; i < 1000000; i++) {
            holder.init();
        }
        System.out.println(holder.array.length);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

When I run this code the program uses up 600 megabytes of RAM. I don't understand why, because when calling the init() method in ArrayHolder the array field is set to the new array and the reference to the old one is lost. Wouldn't that mean that it doesn't take up space anymore? I'm sorry if this is a noobish question but can someone explain why this is and what I'm doing wrong here?

scewps
  • 81
  • 12
  • Your old array would only be kicked off at discretion of gc. If gc runs soon after you reallocate memory then the space would be reclaimed else it wont. – SMA Apr 20 '17 at 08:55
  • large arrays are added to the tenured space and this is only GC-ed by default when it fills up. e.g. sleeping doesn't trigger a GC. In short, creating lots of large arrays like this is bad ideal for so many reasons. – Peter Lawrey Apr 20 '17 at 09:04

2 Answers2

2

Just because your object doesn't have a reference doesn't mean that it won't take up any memory. GC has to run and only then will it be "removed" from memory. You can have objects without references in memory that take up space. Check if GC has run, if not call it explicitly (not a good idea though). Also, I suggest finding if you actually have some Strong references to your Object(s).

You call explicitly call GC using System.gc().

You can add JVM arguments like '-XX:+PrintGCDetails' to check if GC has run.

TheLostMind
  • 34,842
  • 11
  • 64
  • 97
1

array is a reference to an array in Java. In this respect they are like any other regular object derived from java.lang.Object. Once there are no more references to an array, it is scheduled for garbage collection, which need not happen immediately.

It's unlikely that this latency in releasing memory will cause you trouble: the garbage collector will collect unreferred objects when it needs to.

Note that even if the garbage collector does run then there is still no guarantee that the process will hand the memory back to the OS, or the OS will grab the memory back from the process.

Bathsheba
  • 220,365
  • 33
  • 331
  • 451
  • Thanks for pointing that out. How would you go about reducing memory usage for this specific example? Or would you just leave that up to the GC? Because 1 gig seems a bit too much for a simple application. – scewps Apr 20 '17 at 09:01
  • I would simply trust the GC. 1GB is a mere pittance on my 192GB machines I have here, so a 1GB spike is not enough to trigger a GC on my box if the CPUs are busy. – Bathsheba Apr 20 '17 at 09:02
  • I ran the program again with JVisualVM and I performed a garbage collection with it, but the memory usage is not changing. Did the GC just decide to not collect or is that a problem? – scewps Apr 20 '17 at 09:12
  • The process reserves the right not to hand the memory back to the OS. Added to the answer. – Bathsheba Apr 20 '17 at 09:12
  • Okay, thanks for your help. – scewps Apr 20 '17 at 09:15