我们有一个几乎需要实时响应的 Java 应用程序。但我们也看到了长达 8 秒的停顿。
特殊运行条件:
- 在某些时间间隔内,应用程序会将大小高达 1.5G 的巨大数据快照序列化到磁盘 (SSD) 中。
- 偶尔,m/c 中的一些其他共存进程会发生一些繁重的后台 I/O。
我们观察到的是,DURING 在这个大型序列化写入发生的时间间隔内,如果 GC 偶然启动,它会导致巨大的 6 到 8 秒暂停,JVM 完全无响应/卡住状态。
从 JFR 记录中可以看出,
- 除了正在写入磁盘的线程外,所有线程都处于停放/等待/ sleep 状态。
- G1 GC 大约需要 200 毫秒才能完成 GC。
来自 GC 日志:2018-09-05T18:23:40.277+0000: 39892.345:应用程序线程停止的总时间:8.3785112 秒,停止线程耗时:8.3765855 秒
Java 版本: Java 版本“1.8.0_181” Java(TM) SE 运行时环境(build 1.8.0_181-b25) Java HotSpot(TM) 64 位服务器 VM(构建 25.181-b25,混合模式)
问题:
- 当 GC 和后台 I/O 同时发生时,为什么 8 秒的非 GC JVM 暂停?
- 如何克服这个大停顿?
JFR 文件:https://www.filehosting.org/file/details/756217/Run.jfr
最佳答案
您的问题不在 GC 中,而是在 JVM Stop-The-World 机制中 - safepoints . JVM 在开始 GC 相关工作之前等待所有线程停放在安全点。
如果您的 Java 代码使用内存映射文件,您可能需要将其替换为常规 IO。内存映射(尤其是重写)与安全点机制配合不佳。访问内存映射文件的线程可能会被等待写入/读取内存页面的操作系统阻塞。如果此时触发GC,则必须等待IO阻塞线程恢复并到达下一个安全点检查。
更新: 简单来说,如果 Java 线程在 RandomFile
/Channel
读取方法上被阻塞,它不会阻止 JVM 安全点。但是,如果 Java 线程在对内存映射文件进行读/写操作时被阻塞,则 JVM 无法进入安全点,直到线程被解除阻塞。如果这种访问被包裹在循环中,它甚至可能会等到循环在某些条件下完成。
导致长时间“停止线程”的另一个问题可能是循环。如果您有带 int 计数器的简单循环,JVM 认为它“快速”并且可能会忽略循环体内的安全点检查。
关于java - G1 GC - 大型后台 I/O 导致 JVM 无响应 - 8 秒暂停,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52295844/