快速理解Java垃圾回收和jvm中的stw
Java中Stop-The-World機(jī)制簡(jiǎn)稱STW,是在執(zhí)行垃圾收集算法時(shí),Java應(yīng)用程序的其他所有線程都被掛起(除了垃圾收集幫助器之外)。Java中一種全局暫?,F(xiàn)象,全局停頓,所有Java代碼停止,native代碼可以執(zhí)行,但不能與JVM交互;這些現(xiàn)象多半是由于gc引起。
GC時(shí)的Stop the World(STW)是大家最大的敵人。但可能很多人還不清楚,除了GC,JVM下還會(huì)發(fā)生停頓現(xiàn)象。
JVM里有一條特殊的線程--VM Threads,專門用來(lái)執(zhí)行一些特殊的VM Operation,比如分派GC,thread dump等,這些任務(wù),都需要整個(gè)Heap,以及所有線程的狀態(tài)是靜止的,一致的才能進(jìn)行。所以JVM引入了安全點(diǎn)(Safe Point)的概念,想辦法在需要進(jìn)行VM Operation時(shí),通知所有的線程進(jìn)入一個(gè)靜止的安全點(diǎn)。
除了GC,其他觸發(fā)安全點(diǎn)的VM Operation包括:
1. JIT相關(guān),比如Code deoptimization, Flushing code cache ;
2. Class redefinition (e.g. javaagent,AOP代碼植入的產(chǎn)生的instrumentation) ;
3. Biased lock revocation 取消偏向鎖 ;
4. Various debug operation (e.g. thread dump or deadlock check);
監(jiān)控安全點(diǎn)看看JVM到底發(fā)生了什么?
最簡(jiǎn)單的做法,在JVM啟動(dòng)參數(shù)的GC參數(shù)里,多加一句:
-XX:+PrintGCApplicationStoppedTime
它就會(huì)把全部的JVM停頓時(shí)間(不只是GC),打印在GC日志里。
2016-08-22T00:19:49.559+0800: 219.140: Total time for which application threads were stopped: 0.0053630 seconds
這是個(gè)很有用的必配參數(shù),可以打出幾乎一切的停頓……
但是,在JDK1.7.40以前的版本,它居然沒有打印時(shí)間戳,所以只能知道JVM停了多久,但不知道什么時(shí)候停的。此時(shí)一個(gè)土辦法就是加多一句“ -XX:+PrintGCApplicationConcurrentTime”,打印JVM在兩次停頓之間的正常運(yùn)行時(shí)間(同樣沒有時(shí)間戳),但好歹能配合有時(shí)間戳的GC日志,反推出Stop發(fā)生的時(shí)間了。
2016-08-22T00:19:50.183+0800: 219.764: Application time: 5.6240430 seconds
如何打印出事哪種原因?qū)е碌耐nD呢?
再多加兩個(gè)參數(shù):-XX:+PrintSafepointStatistics -XX: PrintSafepointStatisticsCount=1
此時(shí),在stdout中會(huì)打出類似的內(nèi)容
vmop [threads: total initially_running wait_to_block]1913.425: GenCollectForAllocation [ 55 2 0 ] [time: spin block sync cleanup vmop] page_trap_count[ 0 0 0 0 6 ] 0
此日志分兩段,第一段是時(shí)間戳,VM Operation的類型,以及線程概況
total: 安全點(diǎn)里的總線程數(shù)
initially_running: 安全點(diǎn)時(shí)開始時(shí)正在運(yùn)行狀態(tài)的線程數(shù)
wait_to_block: 在VM Operation開始前需要等待其暫停的線程數(shù)
第二行是到達(dá)安全點(diǎn)時(shí)的各個(gè)階段以及執(zhí)行操作所花的時(shí)間,其中最重要的是vmop
spin: 等待線程響應(yīng)
safepoint號(hào)召的時(shí)間
block: 暫停所有線程所用的時(shí)間
sync: 等于 spin+block,這是從開始到進(jìn)入安全點(diǎn)所耗的時(shí)間,可用于判斷進(jìn)入安全點(diǎn)耗時(shí)
cleanup: 清理所用時(shí)間
vmop: 真正執(zhí)行VM Operation的時(shí)間
可見,那些很多但又很短的安全點(diǎn),全都是RevokeBias,詳見 偏向鎖實(shí)現(xiàn)原理, 高并發(fā)的應(yīng)用一般會(huì)干脆在啟動(dòng)參數(shù)里加一句"-XX:-UseBiasedLocking"取消掉它。另外還看到有些類型是no vm operation, 文檔上說(shuō)是保證每秒都有一次進(jìn)入安全點(diǎn)(如果這秒已經(jīng)GC過(guò)就不用了),給一些需要在安全點(diǎn)里進(jìn)行,又非緊急的操作使用,比如一些采樣型的Profiler工具,可用-DGuaranteedSafepointInterval來(lái)調(diào)整,不過(guò)實(shí)際看它并不是每秒都會(huì)發(fā)生,時(shí)間不定。
在實(shí)戰(zhàn)中,我們利用安全點(diǎn)日志,發(fā)現(xiàn)過(guò)有程序定時(shí)調(diào)用Thread Dump等等情況。不過(guò)因?yàn)榘踩c(diǎn)日志默認(rèn)輸出到stdout,因?yàn)樾阅芗皊tdout日志的整潔性等原因,我們平時(shí)默認(rèn)沒有開啟它。只有在需要時(shí)才打開。
再再增加下面三個(gè)參數(shù),可以知道更多VM里發(fā)生的事情。可惜JVM不會(huì)因?yàn)樵O(shè)了這三個(gè)參數(shù),就把安全點(diǎn)日志轉(zhuǎn)移到vm.log里面來(lái),而是白白打印了兩次。
-XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=/dev/shm/vm.log
總結(jié)
本文關(guān)于快速理解Java垃圾回收和jvm中的stw的介紹就到這里,希望對(duì)大家有所幫助,感興趣的朋友可以參閱:淺談Java回收對(duì)象的標(biāo)記和對(duì)象的二次標(biāo)記過(guò)程 、Java虛擬機(jī)裝載和初始化一個(gè)class類代碼解析 、Java中map遍歷方式的選擇問(wèn)題詳解等,有什么問(wèn)題可以隨時(shí)留言,小編會(huì)及時(shí)回復(fù)大家的。
相關(guān)文章
劍指Offer之Java算法習(xí)題精講二叉樹專項(xiàng)訓(xùn)練
跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過(guò)之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化2022-03-03
spring boot項(xiàng)目打包成war在tomcat運(yùn)行的全步驟
這篇文章主要給大家介紹了關(guān)于spring boot項(xiàng)目打包成war在tomcat運(yùn)行的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
詳解SpringBoot和Mybatis配置多數(shù)據(jù)源
本篇文章主要介紹了詳解SpringBoot和Mybatis配置多數(shù)據(jù)源,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動(dòng)填充案例講解
每一次在Controller層中封裝改動(dòng)數(shù)據(jù)的方法時(shí)都要重新設(shè)置一些共性字段,顯得十分冗余。為了解決此問(wèn)題也是在項(xiàng)目中第一次利用到線程,總的來(lái)說(shuō)還是讓我眼前一亮,也開闊了視野,對(duì)以后的開發(fā)具有深遠(yuǎn)的意義2022-10-10
Mybatis 動(dòng)態(tài)sql的編寫與開啟二級(jí)緩存
二級(jí)緩存是Mapper級(jí)別的緩存,多個(gè)SqlSession去操作同一個(gè)Mapper中的SQL語(yǔ)句,則這些SqlSession可以共享二級(jí)緩存,即二級(jí)緩存是跨SqlSession的,這篇文章主要介紹了Mybatis 動(dòng)態(tài)sql的編寫|開啟二級(jí)緩存,需要的朋友可以參考下2023-02-02
Spring Boot集成MyBatis實(shí)現(xiàn)通用Mapper的配置及使用
關(guān)于MyBatis,大部分人都很熟悉。MyBatis 是一款優(yōu)秀的持久層框架,它支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射。這篇文章主要介紹了Spring Boot集成MyBatis實(shí)現(xiàn)通用Mapper,需要的朋友可以參考下2018-08-08
SpringBoot集成Redis的實(shí)現(xiàn)示例
這篇文章主要介紹了SpringBoot集成Redis的實(shí)現(xiàn)示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
java使用udp實(shí)現(xiàn)簡(jiǎn)單多人聊天功能
這篇文章主要為大家詳細(xì)介紹了java使用udp實(shí)現(xiàn)簡(jiǎn)單多人聊天功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
Java通過(guò)Lambda函數(shù)的方式獲取屬性名稱
這篇文章主要介紹了通過(guò)Lambda函數(shù)的方式獲取屬性名稱,實(shí)現(xiàn)步驟是通過(guò)定義一個(gè)函數(shù)式接口, 用來(lái)接收l(shuí)ambda方法引用,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10

