1

In Java, having a nested static class Human, I'm wondering if after cleanup of the map variable can make it available for garbage collected. At the moment just before doSomeCode() I called System.gc() and added Thread.sleep(60000) to wait for garbage collector to pick up unreferenced map contents, but no way - it seems map lives in a program unless it is going to finish. My problem is that I need to free up memory because otherwise will get OutOfMemoryError.

What do you think prevents map property of Human class from being recycled? Is it because the Human class is static and thus all its members can not be garbage collected?

import java.util.List;
import java.util.ArrayList;
import com.carrotsearch.hppc.IntObjectMap;
import com.carrotsearch.hppc.IntObjectOpenHashMap;

public class TestNestedStaticClass {

    public static class Human {

        String name = null;

        List<Human> children = null;

        // some data about family members
        IntObjectMap<int[]> map = null;

        public Human(String name) { this.name = name; }

    }

    public static void main(String[] args) {

        final List<Human> family = new ArrayList<Human>();
        for (int i = 0; i < 1000; i++) {    

            // create and get the family member
            family.add(new Human("givenName"));
            Human h = family.get(i);

            // create map and add some data
            h.map = new IntObjectOpenHashMap<int[]>();
            for (int j = 0; j < 100; j++) {
                int[] array = new int[1500];
                h.map.put(j, array);
            }

        }

        // ...

        // at some point we want to free the memory occupied by
        // family.get(i).map for all i from 0 to 1000, so we do:
        for (int i = 0; i < 1000; i++) {

            // get the family member
            Human h = family.get(i);

            // explicitly remove references from the map
            for (int j = 0; j < 100; j++) {
                h.map.remove(j);
            }

            // cleanup
            h.map.clear();
            h.map = null;

        }

        // ...

        doSomeCode();

    }

}
Sophie Sperner
  • 4,428
  • 8
  • 35
  • 55

6 Answers6

3

From the point where you write: h.map = null;, the map becomes eligible for GC (the removes and clear are technically not necessary).

The reason why you don't see it being GC'ed is possibly due to the fact that you run all that code within the same method, and the GC does not have to collect local variables until the method exits.

If you try to split it in several method it will help the GC (which will get rid of the local variables once the methods exit).

See also this post.

ps: I assumed that you don't have any references to the content of the map or the map itself anywhere else!

Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783
  • Yes, all that code is inside main, but instead of several methods I did explicit blocking like this `{ }` to make go out of scope when the map is not needed. – Sophie Sperner Apr 15 '13 at 17:55
  • @SophieSperner That might not be enough. – assylias Apr 15 '13 at 17:56
  • I will try to split in several methods, but what if it will not help, this means that garbage collection is not so fast as object creation? – Sophie Sperner Apr 15 '13 at 18:10
  • @SophieSperner That is unlikely - if an object can't be created because there is not enough memory, the JVM should try to run the GC first and allocate the new object after. But I would start by making 100% sure that I don't have any references to these objects anywhere (could be a static reference in `IntObjectOpenHashMap` or one of its parent classes for example). – assylias Apr 15 '13 at 18:12
2

The fact that the Human class is static means nothing - and even if the map variable was static, setting it to null frees up the object content for garbage collection.

If you are running into OutOfMemoryErrors, and you are certain the map contents are the cause, then there must be lingering references to the data somewhere. For example, if I do this:

human.map.put(0, new int[10000]);
something.thing = map.get(0);
human.map.remove(0);
human.map = null;

Note that a reference to the int[10000] still remains in memory, @something.thing. Cleaning up the map is only part of the work needed to free up the array for garbage collection.

torquestomp
  • 3,304
  • 19
  • 26
  • Interesting, I do not see such objects, referring to `map` contents. But is it possible to check with some profiler? Something that counts object references. – Sophie Sperner Apr 15 '13 at 18:06
  • Yes, a good profiler will help you to see what objects are hogging all of the heap in your program. We use JProfiler for such things over here – torquestomp Apr 15 '13 at 18:10
  • I would accept your answer as well, but it is allowed to accept only one answer. – Sophie Sperner Apr 15 '13 at 18:13
0

Note that inner static classes are indeed top classes but just inside a class, so the GC rules to collect their instances are the same rules that GC applies on a common class instance.

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
0

There is no need to sleep after System.gc() - the gc method doesn't return until the garbage collector is finished.

An instance of a static inner class is treated the same as an instance of any other class for purposes of garbage collection. You are correctly freeing the map elements of your Human classes (although it's overkill to call map.remove() and map.clear() and map = null - only map = null is needed)

Zim-Zam O'Pootertoot
  • 17,888
  • 4
  • 41
  • 69
  • Here 18Rabbit says `map.remove()` is needed: http://stackoverflow.com/a/154454/655860 – Sophie Sperner Apr 15 '13 at 17:58
  • @SophieSperner Once you have written `map = null` and assuming no other reference to the map ot its content exists, the map and all its content becomes unreachable and therefore eligible for GC. – assylias Apr 15 '13 at 17:59
  • 1
    18Rabbit isn't setting map to null; if he were setting `map = null` then the remove method wouldn't be necessary. – Zim-Zam O'Pootertoot Apr 15 '13 at 17:59
  • An ArrayList is an easier example. If you have 100 objects in an ArrayList, and they're not reachable by any other references, then the garbage collector will traverse the ArrayList and mark its elements as "reachable" and so it won't free them. When the ArrayList is unreachable (by setting all of its references to null, or exiting all methods holding its reference, and so on) then its contents are no longer reachable either, and then the GC will be able to free them - there's no need to remove the contents from the ArrayList first. – Zim-Zam O'Pootertoot Apr 15 '13 at 18:02
0

It looks like you are using all primitive data types. This might be a problem, this is just a theory, and I haven't tested it, but it might be worth a try.

Instead of using an array of int's ( int[] ), try using an ArrayList. This should create objects which can be garbage collected, primitive data types are created on the stack not on the heap of objects, therefore they aren't subject to garbage collection?

NOTE: I put a question mark, because I am not positive, someone can confirm or reject my theory.

THEORY REJECTED :( , LEAVING AROUND FOR OTHERS TO READ

Alex
  • 1,993
  • 1
  • 15
  • 25
-3

1) Been a while since I programmed in Java but my guess is the static objects/variables live on in a special place during whole execution of the program.

2) Calling GC explicitly doesn't ensure the objects will be deleted. You simply call the GC and it decides itself (and you cannot really affect this behavior).

Fiisch
  • 192
  • 9