java - 尽管使用了WeakHashMap,但仍发生OutOfMemoryException

标签 java java-8 garbage-collection out-of-memory weak-references

如果不调用System.gc(),则系统将引发OutOfMemoryException。我不知道为什么我需要显式调用System.gc(); JVM应该自己调用gc(),对吗?请指教。

以下是我的测试代码:

public static void main(String[] args) throws InterruptedException {
    WeakHashMap<String, int[]> hm = new WeakHashMap<>();
    int i  = 0;
    while(true) {
        Thread.sleep(1000);
        i++;
        String key = new String(new Integer(i).toString());
        System.out.println(String.format("add new element %d", i));
        hm.put(key, new int[1024 * 10000]);
        key = null;
        //System.gc();
    }
}

如下所示,添加-XX:+PrintGCDetails以打印出GC信息;如您所见,实际上,JVM尝试执行完整的GC运行,但是失败了;我仍然不知道原因。如果我取消注释System.gc();行,结果是肯定的,这是很奇怪的:
add new element 1
add new element 2
add new element 3
add new element 4
add new element 5
[GC (Allocation Failure) --[PSYoungGen: 48344K->48344K(59904K)] 168344K->168352K(196608K), 0.0090913 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 48344K->41377K(59904K)] [ParOldGen: 120008K->120002K(136704K)] 168352K->161380K(196608K), [Metaspace: 5382K->5382K(1056768K)], 0.0380767 secs] [Times: user=0.09 sys=0.03, real=0.04 secs] 
[GC (Allocation Failure) --[PSYoungGen: 41377K->41377K(59904K)] 161380K->161380K(196608K), 0.0040596 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 41377K->41314K(59904K)] [ParOldGen: 120002K->120002K(136704K)] 161380K->161317K(196608K), [Metaspace: 5382K->5378K(1056768K)], 0.0118884 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at test.DeadLock.main(DeadLock.java:23)
Heap
 PSYoungGen      total 59904K, used 42866K [0x00000000fbd80000, 0x0000000100000000, 0x0000000100000000)
  eden space 51712K, 82% used [0x00000000fbd80000,0x00000000fe75c870,0x00000000ff000000)
  from space 8192K, 0% used [0x00000000ff800000,0x00000000ff800000,0x0000000100000000)
  to   space 8192K, 0% used [0x00000000ff000000,0x00000000ff000000,0x00000000ff800000)
 ParOldGen       total 136704K, used 120002K [0x00000000f3800000, 0x00000000fbd80000, 0x00000000fbd80000)
  object space 136704K, 87% used [0x00000000f3800000,0x00000000fad30b90,0x00000000fbd80000)
 Metaspace       used 5409K, capacity 5590K, committed 5760K, reserved 1056768K
  class space    used 576K, capacity 626K, committed 640K, reserved 1048576K

最佳答案

JVM会自行调用GC,但在这种情况下,它太少了,太迟了。
在这种情况下,清除内存的不仅是GC。
映射值很容易达到,并且在对映射值进行某些操作时会被映射本身清除。

如果您打开GC事件(XX:+ PrintGC),则输出如下:

add new element 1
add new element 2
add new element 3
add new element 4
add new element 5
add new element 6
add new element 7
[GC (Allocation Failure)  2407753K->2400920K(2801664K), 0.0123285 secs]
[GC (Allocation Failure)  2400920K->2400856K(2801664K), 0.0090720 secs]
[Full GC (Allocation Failure)  2400856K->2400805K(2590720K), 0.0302800 secs]
[GC (Allocation Failure)  2400805K->2400805K(2801664K), 0.0069942 secs]
[Full GC (Allocation Failure)  2400805K->2400753K(2620928K), 0.0146932 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

直到最后一次尝试将值(value)放入映射中时,才会触发GC。

直到映射键出现在引用队列上,WeakHashMap才能清除陈旧的条目。
映射键只有在被垃圾回收后才会出现在引用队列上。
在 map 有机会自行清除之前,会触发为新 map 值分配内存的操作。
当内存分配失败并触发GC时,将收集映射键。但这太少了,太晚了-没有足够的内存被释放来分配新的映射值。
如果减少有效负载,则可能最终会获得足够的内存来分配新的映射值,并且过时的条目将被删除。

另一种解决方案是将值本身包装到WeakReference中。这将允许GC清除资源,而无需等待 map 自行完成。
这是输出:
add new element 1
add new element 2
add new element 3
add new element 4
add new element 5
add new element 6
add new element 7
[GC (Allocation Failure)  2407753K->2400920K(2801664K), 0.0133492 secs]
[GC (Allocation Failure)  2400920K->2400888K(2801664K), 0.0090964 secs]
[Full GC (Allocation Failure)  2400888K->806K(190976K), 0.1053405 secs]
add new element 8
add new element 9
add new element 10
add new element 11
add new element 12
add new element 13
[GC (Allocation Failure)  2402096K->2400902K(2801664K), 0.0108237 secs]
[GC (Allocation Failure)  2400902K->2400838K(2865664K), 0.0058837 secs]
[Full GC (Allocation Failure)  2400838K->1024K(255488K), 0.0863236 secs]
add new element 14
add new element 15
...
(and counting)

好多了。

关于java - 尽管使用了WeakHashMap,但仍发生OutOfMemoryException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60584806/

相关文章:

java - 究竟什么被认为是垃圾收集根,它们是如何在 HotSpot JVM 中找到的?

java - 无法在 Fedora 14 上安装 JDK 1.7

java - Postgres JDBC 驱动程序未返回错误行号,如 PGAdmin 中所示

java - 如何重新使用一些 "Eclipse IDE plugins"创建 Eclipse RCP 应用程序?

java-8 - 列出 Java 8 中的类

java - Tree实现java中递归函数调用转换为lambda表达式

java - 让mysql和java一起工作

java - 为什么我必须在 Java 中链接 Stream 操作?

c# - 为什么最后才调用 GC.KeepAlive,而不是最开始?

Android中ART垃圾回收暂停时间是指​​主线程还是工作线程?