调用System.gc后为何需Thread.sleep?JDK本地内存场景疑问
FileChannelImpl中System.gc后Thread.sleep的必要性分析
研究Java 17中System.gc的源码实现后,能明确它触发的GC分为两种类型:
- Full GC(Stop The World):使用SerialGC、ParallelGC、ZGC时,调用
System.gc的Java线程会被阻塞,直到Full GC完全完成才会继续执行 - 并发Full GC:使用G1、Shenandoah且开启
-XX:+ExplicitGCInvokesConcurrent参数时,调用线程触发GC后会快速返回,无需等待GC完成
现在聚焦阻塞式Full GC的场景:既然调用System.gc的线程会卡在本地代码的安全点,直到GC结束才返回,那FileChannelImpl的mapInternal方法里,在System.gc()之后调用Thread.sleep(100)是否真的有必要?
先看对应的代码片段:
public class FileChannelImpl extends FileChannel { private Unmapper mapInternal(MapMode mode, long position, long size, int prot, boolean isSync) throws IOException { try { // If map0 did not throw an exception, the address is valid addr = map0(prot, mapPosition, mapSize, isSync); } catch (OutOfMemoryError x) { // An OutOfMemoryError may indicate that we've exhausted // memory so force gc and re-attempt map System.gc(); try { // do We really need Thread.sleep here ? Thread.sleep(100); } catch (InterruptedException y) { Thread.currentThread().interrupt(); } try { addr = map0(prot, mapPosition, mapSize, isSync); } catch (OutOfMemoryError y) { // After a second OOME, fail throw new IOException("Map failed", y); } } } }
从Java 17的GC实现逻辑来看,当触发的是阻塞式Full GC时,System.gc()调用本身就是同步阻塞的——线程必须等到GC完成、内存回收完毕后才会从该方法返回。此时理论上已经满足了重试内存映射的内存条件,额外的Thread.sleep(100)完全是多余的:
- 它不会带来任何内存回收的额外收益,因为GC已经完成
- 反而会增加不必要的延迟,拖慢重试流程
至于这段sleep存在的可能原因,大概率是历史遗留:早期JDK版本中,System.gc的实现可能存在同步性不足的问题,或者考虑到堆外内存回收依赖Finalizer线程的异步执行。但在Java 17的主流收集器实现下,这些场景已经不存在。
结论:在Java 17的阻塞式Full GC场景下,此处的Thread.sleep确实没有保留的必要,可以移除。
内容的提问来源于stack exchange,提问作者bin
相关产品推荐
相关产品推荐

