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()
会适得其反,因为那时应用程序会花费更多时间进行垃圾收集以及创建和处理OutOfMemoryError
s。
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() 调用是否很糟糕?的主要内容,如果未能解决你的问题,请参考以下文章