G1 GC-大型后台I / O导致JVM无法响应-暂停8秒

时间:2018-09-12 12:59:13

标签: java garbage-collection pause g1gc

我们有一个Java应用程序,几乎需要实时响应。但我们也看到停顿了8秒。

特殊运行条件:

  1. 在某些时间间隔内,应用程序会将一个最大为1.5G的巨大数据快照序列化到磁盘(SSD)中。
  2. m / c中某些其他共存过程有时会发生一些沉重的背景I / O。

我们正在观察的情况,期间,发生这种大序列化写入的时间间隔,如果偶然发生GC启动,这会导致6到8秒的巨大暂停,则JVM完全没有响应/冻结状态。

从JFR记录显示,

  1. 除正在写入磁盘的线程外,所有线程均处于驻留/等待/睡眠状态。
  2. G1 GC完成GC大约需要200毫秒。

来自GC的日志:2018-09-05T18:23:40.277 + 0000:39892.345:停止应用程序线程的总时间: 8.3785112 秒,停止线程所需的时间:8.3765855秒

Java版本: Java版本“ 1.8.0_181” Java(TM)SE运行时环境(内部版本1.8.0_181-b25) Java HotSpot(TM)64位服务器VM(内部版本25.181-b25,混合模式)

问题:

  1. 当GC和后台I / O同时发生时,为什么这样的8秒非GC JVM暂停?
  2. 如何克服这一大停顿?

JFR文件:https://www.filehosting.org/file/details/756217/Run.jfr

1 个答案:

答案 0 :(得分:1)

您的问题不在GC中,而是JVM Stop-The-World机制-safepoints。 JVM等待所有线程停在安全点上,然后再开始与GC相关的工作。

如果您的Java代码正在使用内存映射文件,则可能需要用常规IO替换它。内存映射(尤其是繁重的写操作)在安全点机制中起着非常不利的作用。等待写/读内存页面的操作系统可能阻止线程访问内存映射文件。如果此时触发了GC,则必须等待IO阻塞线程才能恢复并到达下一个安全点检查。

更新:简单来说,如果Java线程在RandomFile / Channel读取方法上被阻止,则不会阻止JVM安全点。但是如果Java线程在对内存映射文件的读/写操作上被阻止,则JVM在线程被取消阻止之前无法进入安全点。如果将此类访问包装在循环中,则它甚至可能在某些情况下等到循环结束。

另一个导致长时间“停止线程占用”的问题可能是循环。如果您具有带有int计数器的简单循环,则JVM会将其视为“快速”的,并且可以在循环体内省略安全点检查。