问题
The Java code is as follows:
Random r = new Random(1234697890);
HashMap<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<100000;i++){
for(int j=0;j<1000;j++){
list.add(r.nextInt(100000));
}
map.put(i, list);
map.remove(i);
}
when i
reaches 37553 , java.lang.OutOfMemoryError: Java heap space
happens.
It seems that garbage collection does not happen in the loop.
Now I wonder how to fix the problem.
回答1:
Try rewriting the code as follows and you should not get OOME's ...
Random r = new Random(1234697890);
HashMap<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>();
for(int i=0;i<100000;i++){
List<Integer> list = new ArrayList<Integer>();
for(int j=0;j<1000;j++){
list.add(r.nextInt(100000));
}
map.put(i, list);
map.remove(i);
}
The problem with your original code is that:
- you only create one list,
- you keep adding more and more elements to it, and
- that list only becomes garbage when the code completes ... because it is "in scope" the whole time.
Moving the list
declaration inside the loop means that a new ArrayList
is created and filled in each loop iteration, and becomes garbage when you start the next iteration.
Someone suggested calling System.gc()
. It won't help at all in your case because there is minimal1 garbage to be collected. And in general it is a bad idea because:
- the GC is guaranteed to have run immediately before an OOME is thrown,
- the JVM can figure out better than you can when is the best (i.e. most efficient) time to run the GC,
- your call to
System.gc()
may be totally ignored anyway. The JVM can be configured so that calls toSystem.gc()
are ignored.
1 - The pedant in me would like to point out that map.put(i, list); map.remove(i);
is most likely generating an Integer
object that most likely becomes garbage. However, this is "chicken feed" compared to your indefinitely growing ArrayList
object.
回答2:
You use the same List all the time, which contains 100000 * 1000 items when the loop exits. To enable GC to get rid of your list, you need to reduce its scope to within the for(i)
loop.
In other words, both map and list are reachable at all time in that piece of code and are therefore not eligible for collection.
回答3:
In your case you keep filling the same list
(even though you remove it from that HashMap, it still exists as a local variable).
The JVM promises to do a full garbage collect before throwing an OutOfMemoryError
. So you can be sure there is nothing left to clean up.
回答4:
Your code
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<100000;i++){
for(int j=0;j<1000;j++){
list.add(r.nextInt(100000));
}
map.put(i, list);
map.remove(i);
}
is the same as
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<100000 * 1000; i++) {
list.add(r.nextInt(100000));
}
As you can see its the List, not the map which is retaining all the integers. BTW Try this instead and see what happens ;)
list.add(r.nextInt(128));
来源:https://stackoverflow.com/questions/13723983/does-garbage-collection-happen-immediately-after-hashmap-remove-is-called