sun.nio.ch.FileChannelImpl 中的 System.gc() 调用是否很糟糕?

Posted

技术标签:

【中文标题】sun.nio.ch.FileChannelImpl 中的 System.gc() 调用是否很糟糕?【英文标题】: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 【参考方案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 的一个建议。不能保证垃圾收集器通过此调用释放任何内存。

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

【讨论】:

以上是关于sun.nio.ch.FileChannelImpl 中的 System.gc() 调用是否很糟糕?的主要内容,如果未能解决你的问题,请参考以下文章