【问题标题】:Is the System.gc() call in sun.nio.ch.FileChannelImpl a bad case?sun.nio.ch.FileChannelImpl 中的 System.gc() 调用是不是很糟糕?
【发布时间】:2022-01-24 05:22:08
【问题描述】:
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

这对异常恢复有帮助吗?

【问题讨论】:

  • JRE 的 System.gc() 是无操作的,因此如果您的 JRE 是其中之一,它不会改变任何事情。在抛出异常之前,JRE 也可能已经尝试执行 gc。
  • 这是一种特殊情况,因为垃圾收集器不跟踪本机内存,但本机缓冲区的清理取决于垃圾收集器检测到前端的不可达性ByteBuffer

标签: java java-8 garbage-collection


【解决方案1】:

当对象分配失败并返回 OutOfMemoryError 时,垃圾收集器已尽最大努力回收未使用对象的内存或扩展堆。所以在捕捉到OutOfMemoryError 之后调用System.gc() 将毫无意义。

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


FileChannelImpl 的情况不同。 map0 可能由于本机内存不足或在 32 位系统的情况下地址空间不足而失败。在这些情况下,堆内存管理器没有生成OutOfMemoryError,并且垃圾收集器可能没有运行。但是要回收本机内存或地址空间,关联的ByteBuffer 实例必须进行垃圾回收,以便它们的清理器可以运行。这是一个罕见的极端情况,调用System.gc(); 是有意义的。

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

【讨论】:

  • 我从 Pangin 那里听到了一个非常好的演讲,他详细说明了目前没有其他方法可以释放本机内存,除了在发生这种情况时调用 System.gc。那是我们开始禁用显式 gc VM 选项的那一刻。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 2011-10-06
    • 2011-09-20
    • 1970-01-01
    • 2010-12-09
    • 2010-12-16
    • 2018-01-20
    • 2010-10-30
    • 1970-01-01
    • 2015-10-14
    相关资源
    最近更新 更多