一道面试题引发的对 Java 内存模型的一点疑问

 阅读约 8 分钟

一道面试题引发的对Java内存模型的一点疑问

问题描述
file

如上图所示程序,按道理,子线程会通过 num++ 操作破坏 while 循环的条件,从而终止循环,执行最后的输出操作。但在我的多次运行中,偶尔会出现 while 循环一直不结束的场合。像我截图一样,程序一直不终止,JDK7、JDK8 均已试验,均能偶然触发。

回复

[西湖の风]:变量前加个 volatile。

[csyangchsh]:volatile 使用读写屏障强制刷新缓存,如果不加就由 CPU 决定何时刷新。

[sofkyle]:由 CPU 决定何时刷新,那么可以认为,终会有一个时机会去刷新,但是while卡在那一直不刷新。

[你假笨]:和 jit 也是有一定关系的,-Xint 设定解释执行,也可以只关闭 OSR 看看,-XX:-UseOnStackReplacement。

JVM STW 里的 no vm operation 是怎么发生的

问题描述
file
我们线上应用提供的服务接口突然超时(dubbo服务接口调用耗时最大限制1s),发现gc.log在对应的时间进入了STW,耗时1.526s查看对应的vm.log发现在相对饮的时间有一个no vm operation提示,请教下:no vm operation,这个类型vm具体在做什么操作啊?如何优化?

回复

[你假笨]:safepoint并不是一定要发生了某个VM_OP才会进入的,VMThread本身的执行,就是不断循环,看是否有必要进入safepoint,或者是否要执行一些VM_OP,可以看下这块代码。

while (!should_terminate() && _cur_vm_operation == NULL) {
        // wait with a timeout to guarantee safepoints at regular intervals
        bool timedout =
          VMOperationQueue_lock->wait(Mutex::_no_safepoint_check_flag,
                                      GuaranteedSafepointInterval);

        // Support for self destruction
        if ((SelfDestructTimer != 0) && !is_error_reported() &&
            (os::elapsedTime() > SelfDestructTimer * 60)) {
          tty->print_cr("VM self-destructed");
          exit(-1);
        }

        if (timedout && (SafepointALot ||
                         SafepointSynchronize::is_cleanup_needed())) {
          MutexUnlockerEx mul(VMOperationQueue_lock,
                              Mutex::_no_safepoint_check_flag);
          // Force a safepoint since we have not had one for at least
          // 'GuaranteedSafepointInterval' milliseconds.  This will run all
          // the clean-up processing that needs to be done regularly at a
          // safepoint
          SafepointSynchronize::begin();
          #ifdef ASSERT
            if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot();
          #endif
          SafepointSynchronize::end();
        }
        _cur_vm_operation = _vm_queue->remove_next();

        // If we are at a safepoint we will evaluate all the operations that
        // follow that also require a safepoint
        if (_cur_vm_operation != NULL &&
            _cur_vm_operation->evaluate_at_safepoint()) {
          safepoint_ops = _vm_queue->drain_at_safepoint_priority();
        }
      }

几个关键的点,GuaranteedSafepointInterval,timedout,SafepointSynchronize::is_cleanup_needed()
如果上面条件满足,就会发生 no vm operation 的 safepoint 操作。

通过GarbageCollectorMXBean获取到的fgc次数耗时与jstat获取到的不一致

问题描述

-XX:+UseCompressedOops -Xms5g -Xmx5g -XX:PermSize=256M -XX:MaxPermSize=1024m -XX:NewSize=3g -XX:MaxNewSize=3g -XX:+UseCMSInitiatingOccupancyOnly -XX:+PerfDataSaveToFile -XX:SurvivorRatio=10 -Xloggc:/data/dataLogs/gc/gc.log -verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+HeapDumpOnOutOfMemoryError
如题,这是我的 jvm 参数,jstat采集到的ygc次数与mxbean是一致的,fgc的数量大概是mxbean统计到的两倍,但是不到两倍。
对于耗时,jstat采集到的无论是ygc还是fgc均小于mxbean统计到的数据。

回复

[Rookie_267692]:这是因为CMS收集器在MXBean是在每次发生FGC时只会在Sweeping统计一次,而jstat会在InitialMark阶段统计一次,FinalMark阶段统计一次,这样发生一次CMS gc时就会统计两次,所以次数不一致。
gc时间在MXBen中统计的是整个gc从开始到结束时间,jstat统计的是gc在每个阶段实际耗费的时间。
file

推荐阅读 ZGC什么时候会进行垃圾回收
推荐阅读 GC一些长时间停顿问题排查及解决办法
阅读 107发布于 1月15日

推荐阅读
目录