快速理解Java垃圾回收和jvm中的stw

  

Java中停止一切机制简称<强> STW 强,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外). Java中一种全局暂停现象,全局停顿,所有Java代码停止,原生代码可以执行,但不能与JVM交互;这些现象多半是由于gc引起。
  

  

GC时的停止世界(STW)是大家最大的敌人。但可能很多人还不清楚,除了GC, JVM下还会发生停顿现象。
  

  

JVM里有一条特殊的线程——VM线程,专门用来执行一些特殊的VM操作,比如分派,GC线程转储等,这些任务,都需要整个堆,以及所有线程的状态是静止的,一致的才能进行。所JVM以引入了安全点(安全点)的概念,想办法在需要进行VM操作时,通知所有的线程进入一个静止的安全点。
  

  

除了GC,其他触发安全点的VM操作包括:
  

  

1。JIT相关,比如逆优化代码,冲洗代码缓存;
  

  

2。类重新定义(例如javaagent, AOP代码植入的产生的仪表);
  

  

3。偏向锁撤销取消偏向锁;
  

  

4。各种调试操作(例如线程转储或死锁止);
  

  

监控安全点看看JVM到底发生了什么?
  

  

最简单的做法,在JVM启动参数的GC参数里,多加一句:
  

  

- xx: + PrintGCApplicationStoppedTime
  

  

它就会把全部的JVM停顿时间(不只是GC),打印在GC日志里。
  

  

<强> 2016 - 08 - 22 - t00:19:49.559 + 0800: 219.140:哪个应用程序线程停止总时间:0.0053630秒
  

  

这是个很有用的必配参数,可以打出几乎一切的停顿……
  

  

但是,在JDK1.7.40以前的版本,它居然没有打印时间戳,所以只能知道JVM停了多久,但不知道什么时候停的。此时一个土办法就是加多一句“- xx: + PrintGCApplicationConcurrentTime”,打印JVM在两次停顿之间的正常运行时间(同样没有时间戳),但好歹能配合有时间戳的GC日志,反推出停止发生的时间了。
  

  

<强> 2016 - 08 - 22 - t00:19:50.183 + 0800: 219.764:申请时间:5.6240430秒
  

  

如何打印出事哪种原因导致的停顿呢?
  

  

再多加两个参数<强>:- xx: + PrintSafepointStatistics - xx: PrintSafepointStatisticsCount=1
  

  

此时,在stdout中会打出类似的内容

  

<强> vmop[线程:总initially_running wait_to_block] 1913.425: GenCollectForAllocation[55 2 0][时间:自旋阻塞同步清理vmop] page_trap_count [0 0 0 0 6] 0
  

  

此日志分两段,第一段是时间戳,VM操作的类型,以及线程概况
  

  

总:安全点里的总线程数
  

  

initially_running:安全点时开始时正在运行状态的线程数
  

  

wait_to_block:在VM操作开始前需要等待其暂停的线程数
  

  

第二行是到达安全点时的各个阶段以及执行操作所花的时间,其中最重要的是vmop
  

  

旋转:等待线程响应
  

  

safepoint号召的时间
  

  

块:暂停所有线程所用的时间
  

  

同步:等于自旋+块,这是从开始到进入安全点所耗的时间,可用于判断进入安全点耗时
  

  

清理:清理所用时间
  

  

vmop:真正执行VM操作的时间
  

  

可见,那些很多但又很短的安全点,全都是RevokeBias,详见偏向锁实现原理,高并发的应用一般会干脆在启动参数里加一句- xx:”及“取消掉它。另外还看到有些类型是没有vm操作,文档上说是保证每秒都有一次进入安全点(如果这秒已经GC过就不用了),给一些需要在安全点里进行,又非紧急的操作使用,比如一些采样型的分析器工具,可用-DGuaranteedSafepointInterval来调整,不过实际看它并不是每秒都会发生,时间不定。
  

  

在实战中,我们利用安全点日志,发现过有程序定时调用线程转储等等情况。不过因为安全点日志默认输出到stdout,因为性能及stdout日志的整洁性等原因,我们平时默认没有开启它。只有在需要时才打开。
  

  

再再增加下面三个参数,可以知道更多VM里发生的事情。可惜JVM不会因为设了这三个参数,就把安全点日志转移到VM。日志里面来,而是白白打印了两次。
  

  

<强> - xx: + UnlockDiagnosticVMOptions - xx: + LogVMOutput - xx:日志文件=/dev/shm/vm.log

  

  

本文关于快速理解Java垃圾回收和jvm中的stw的介绍就到这里,希望对大家有所帮助,感兴趣的朋友可以参阅:浅谈Java回收对象的标记和对象的二次标记过程,,Java虚拟机装载和初始化一个类类代码解析,,Java中图遍历方式的选择问题详解等,有什么问题可以随时留的言,小编会及时回复大家的。

快速理解Java垃圾回收和jvm中的stw