sun.nio.ch.FileChannelImpl 中的 System.gc() 调用是坏情况吗?

Is the System.gc() call in sun.nio.ch.FileChannelImpl a bad case?

try {
    // If no exception was thrown from map0, the address is valid
    addr = map0(imode, mapPosition, mapSize);
} catch (OutOfMemoryError x) {
    // An OutOfMemoryError may indicate that we've exhausted memory
    // so force gc and re-attempt map
    System.gc();
    try {
        Thread.sleep(100);
    } catch (InterruptedException y) {
        Thread.currentThread().interrupt();
    }
    try {
        addr = map0(imode, mapPosition, mapSize);
    } catch (OutOfMemoryError y) {
        // After a second OOME, fail
        throw new IOException("Map failed", y);
    }
}

来自 jdk/FileChannelImpl.java at jdk8-b120.

这对异常恢复有帮助吗?

举个例子,问题是内存不足。因此,运行 垃圾收集器释放内存可能会解决这个问题。但是对 System.gc() 的调用只是对 JVM 的建议。不能保证垃圾收集器通过此调用释放任何内存。

所以这种方法有点启发式。

当对象分配失败并显示 OutOfMemoryError 时,垃圾收集器确实已经尽力回收未使用对象的内存或扩展堆。所以在捕获 OutOfMemoryError 之后调用 System.gc() 是没有意义的。

一种特殊情况是垃圾收集器反复回收少量内存,因此应用程序可以继续,但必须在下一次分配时执行另一次垃圾收集。当某些垃圾收集器检测到应用程序花费了超过 98% 的 CPU 时间用于垃圾收集时,它们会抛出 OutOfMemoryError 消息“超出 GC 开销限制”。在这种情况下,调用 System.gc() 会适得其反,因为那样的话,应用程序会花费更多的时间在垃圾收集以及创建和处理 OutOfMemoryError 上。


FileChannelImpl的情况不同。 map0 可能由于本机内存不足而失败,或者在 32 位系统的情况下,当 运行 超出地址 space 时。在这些情况下,堆内存管理器没有产生 OutOfMemoryError,垃圾收集器也可能没有产生 运行。但是要回收本机内存或地址 space,关联的 ByteBuffer 实例必须进行垃圾回收,因此它们的清理器可以 运行。这是一个罕见的角落案例,其中调用 System.gc(); 是有意义的。

它仍然很脆弱,因为 System.gc(); 不能保证收集所有对象或 运行 垃圾收集器。 JEP 383 应该通过更好地控制本机分配的生命周期来解决这个问题。