一、概述可達(dá)性性分析中從GC Roots節(jié)點找引用鏈這個操作,,可作為GC Roots的節(jié)點主要在全局性的引用(如常量或類靜態(tài)屬性)與執(zhí)行上下文(如棧幀中的本地變量表)中,,現(xiàn)在很多應(yīng)用僅僅方法區(qū)就有數(shù)百兆,如果要逐個檢查這里面的引用,,那么必然會消耗很多時間,。 另外,可達(dá)性分析對執(zhí)行事件的敏感還體現(xiàn)在GC停頓上,,因為這項分析工作必須在一個能確保一致性的快照中進(jìn)行,。一致性是指整個分析期間整個執(zhí)行系統(tǒng)看起來就像被凍結(jié)在某個時間點上,不可以出現(xiàn)在分析過程中對象引用關(guān)系還在不斷變化的情況,。該點不滿足的話分析結(jié)果準(zhǔn)確性就無法得到保證,。這點是導(dǎo)致GC進(jìn)行時必須停頓所有java線程的其中一個重要原因(Sun稱之為Stop-The-World),即使是在號稱(幾乎)不會發(fā)生停頓的CMS收集器中,,枚舉跟節(jié)點時也是必須要停頓的,。 在主流的JVM使用的是準(zhǔn)確式GC,所以當(dāng)執(zhí)行系統(tǒng)停頓下來后,,并不需要一個不漏地檢查所有執(zhí)行上下文和全局的引用位置,,虛擬機應(yīng)當(dāng)是有辦法直接得知哪些地方存放著對象引用。 在HotSpot實現(xiàn)中,,是使用一組稱為OopMapde 的數(shù)據(jù)結(jié)構(gòu)來達(dá)到這個目的,,在類加載完的時候,HotSpot就把對象內(nèi)什么偏移量上是什么類型的數(shù)據(jù)計算出來,,在JIT編譯過程中,,也會在特定位置記錄下棧和寄存器中哪些位置是引用。這樣,,GC掃碼時就可以直接指導(dǎo)這些信息,。 1.1、安全點安全點的選定基本上是以程序”是否具有讓程序長時間執(zhí)行的特征“為標(biāo)準(zhǔn)進(jìn)行選定的,。長時間執(zhí)行,,明顯的特征就是指令序列復(fù)用,如方法調(diào)用,,循環(huán)跳轉(zhuǎn),異常跳轉(zhuǎn)等,,所以具有這些功能的指令才會產(chǎn)生Safepoint,。 對于安全點,如何在GC發(fā)生時讓所有線程(不包括JNI調(diào)用的線程)都跑到最近的安全點上在停頓下來,。兩種方案:強先式中斷和主動式中斷,。 強先式中斷,不需要線程的執(zhí)行代碼主動去配合,在GC發(fā)生時,,首先把所有線程全部中斷,,如果發(fā)現(xiàn)有線程中斷的地方不在安全點上,就恢復(fù)線程,,讓他跑到安全點?,F(xiàn)代JVM,幾乎不使用 主動式中斷,,需要中斷線程的時候,,不直接對線程操作,僅僅簡單地設(shè)置一個標(biāo)志,,各個線程執(zhí)行時主動去輪詢這個標(biāo)志,,發(fā)現(xiàn)中斷標(biāo)志為真時就自己中斷掛起。輪詢標(biāo)志的地方和安全點是重合的,,另外再加上創(chuàng)建對象需要分配的內(nèi)存的地方,。 1.2、安全區(qū)域Safepoint機制保證了程序執(zhí)行時,,在不太長的時間內(nèi)就會遇到可進(jìn)入GC的Safepoint,。但是程序“不執(zhí)行”的時候呢?沒有分配時間片,,如線程Sleep或Blocked狀態(tài),,這時線程無法想用JVM的中斷請求,也無法去中斷,,這時需要安全區(qū)域(Safe Region)來解決,。 安全區(qū)域是指在一段代碼片段中,引用關(guān)系不會發(fā)生變化,。在這個區(qū)域中的任意地方開始GC都是安全的,,擴(kuò)展了的Safepoint。 1.3,、java對象內(nèi)存申請過程007-對象內(nèi)存分配與回收,垃圾收集過程,,簡單敘述如下: 1.JVM會試圖為相關(guān)Java對象在Eden中初始化一塊內(nèi)存區(qū)域;當(dāng)Eden空間足夠時,,內(nèi)存申請結(jié)束,。否則到下一步; 2.JVM試圖釋放在Eden中所有不活躍的對象(minor collection),,釋放后若Eden空間仍然不足以放入新對象,,則試圖將部分Eden中活躍對象放入Survivor區(qū); 3.Survivor區(qū)被用來作為Eden及old的中間交換區(qū)域,,當(dāng)old區(qū)空間足夠時,,Survivor區(qū)的對象會被移到Old區(qū),否則會被保留在Survivor區(qū); 4.當(dāng)old區(qū)空間不夠時,,JVM會在old區(qū)進(jìn)行major collection,; 5.垃圾收集后,若Survivor及old區(qū)仍然無法存放從Eden復(fù)制過來的部分對象,,導(dǎo)致JVM無法在Eden區(qū)為新對象創(chuàng)建內(nèi)存區(qū)域,,則出現(xiàn)"Out of memory錯誤"; 1.4,、STP(Stop-The-World)誘因在新生代進(jìn)行的GC叫做minor GC,,在老年代進(jìn)行的GC都叫major GC,F(xiàn)ull GC同時作用于新生代和老年代,。在垃圾回收過程中經(jīng)常涉及到對對象的挪動(比如上文提到的對象在Survivor 0和Survivor 1之間的復(fù)制),,進(jìn)而導(dǎo)致需要對對象引用進(jìn)行更新。為了保證引用更新的正確性,,Java將暫停所有其他的線程,,這種情況被稱為“Stop-The-World”,導(dǎo)致系統(tǒng)全局停頓,。Stop-The-World對系統(tǒng)性能存在影響,,因此垃圾回收的一個原則是盡量減少“Stop-The-World”的時間。 不同垃圾收集器的Stop-The-World情況,,Serial,、Parallel和CMS收集器均存在不同程度的Stop-The-Word情況;而即便是最新的G1收集器也不例外,。
1.Biased lock revocation 取消偏向鎖 2. Class redefinition (e.g. javaagent,,AOP的代碼植入) 3. Various debug operation (e.g. thread dump 一條或所有線程,,heapduump等) 打開JDK8源碼的vm_operations.hpp,有大概完整58項列表,。 1.5、STP(Stop-The-World)四個階段根據(jù)-XX:+PrintSafepointStatistics –XX:PrintSafepointStatisticsCount=1 參數(shù)虛擬機打印的日志文件可以看出,safepoint的執(zhí)行一共可以分為四個階段: Spin階段,。因為jvm在決定進(jìn)入全局safepoint的時候,,有的線程在安全點上,而有的線程不在安全點上,,這個階段是等待未在安全點上的用戶線程進(jìn)入安全點,。 Block階段。即使進(jìn)入safepoint,,用戶線程這時候仍然是running狀態(tài),,保證用戶不在繼續(xù)執(zhí)行,需要將用戶線程阻塞,。http://blog.csdn.net/iter_zc/article/details/41892567這篇bog詳細(xì)說明了如何將用戶線程阻塞,。 Cleanup。這個階段是JVM做的一些內(nèi)部的清理工作,。 VM Operation. JVM執(zhí)行的一些全局性工作,,例如GC,代碼反優(yōu)化。 常用JVM參數(shù):
-XX:+PrintGCApplicationStoppedTime ?。篔VM的停頓時間(不只是GC),,打印在GC日志里。 -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 這兩個參數(shù)用于記錄STW發(fā)生的原因,、線程情況,、STW各個階段的停頓時間等。當(dāng)添加這兩個參數(shù)后,,程序運行期間會打印如下內(nèi)容 如: Total time for which application threads were stopped: 0.0053256 seconds, Stopping threads took: 0.0000221 seconds time:14464 [GC (Allocation Failure) [DefNew: 959K->64K(960K), 0.0045369 secs] 523263K->523259K(524224K), 0.0045726 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] vmop [threads: total initially_running wait_to_block] [time: spin block sync cleanup vmop] page_trap_count 14.610: GenCollectForAllocation [ 13 0 0 ] [ 0 0 0 0 4 ] 0 Total time for which application threads were stopped: 0.0047146 seconds, Stopping threads took: 0.0000224 seconds [GC (Allocation Failure) [DefNew: 960K->960K(960K), 0.0000229 secs][Tenured: 523195K->523263K(523264K), 0.4558175 secs] 524155K->524154K(524224K), [Metaspace: 3842K->3842K(1056768K)], 0.4559020 secs] [Times: user=0.45 sys=0.00, real=0.46 secs] vmop [threads: total initially_running wait_to_block] [time: spin block sync cleanup vmop] page_trap_count 14.636: GenCollectForAllocation [ 13 0 0 ] [ 0 0 0 0 455 ] 0 Total time for which application threads were stopped: 0.4560633 seconds, Stopping threads took: 0.0000231 seconds time:14962 [Full GC (Allocation Failure) [Tenured: 523263K->523263K(523264K), 0.4344774 secs] 524223K->524221K(524224K), [Metaspace: 3842K->3842K(1056768K)], 0.4345167 secs] [Times: user=0.43 sys=0.01, real=0.43 secs] vmop [threads: total initially_running wait_to_block] [time: spin block sync cleanup vmop] page_trap_count 15.094: GenCollectForAllocation [ 13 0 0 ] [ 0 0 0 0 434 ] 0 Total time for which application threads were stopped: 0.4346385 seconds, Stopping threads took: 0.0000174 seconds [Full GC (Allocation Failure) [Tenured: 523263K->523263K(523264K), 0.4154644 secs] 524223K->524223K(524224K), [Metaspace: 3842K->3842K(1056768K)], 0.4154981 secs] [Times: user=0.41 sys=0.00, real=0.42 secs] [Full GC (Allocation Failure) [Tenured: 523263K->515874K(523264K), 0.4559588 secs] 524223K->515874K(524224K), [Metaspace: 3842K->3842K(1056768K)], 0.4559872 secs] [Times: user=0.46 sys=0.00, real=0.46 secs] vmop [threads: total initially_running wait_to_block] [time: spin block sync cleanup vmop] page_trap_count 15.529: GenCollectForAllocation [ 13 0 0 ] [ 0 0 0 0 871 ] 0 Total time for which application threads were stopped: 0.8716535 seconds, Stopping threads took: 0.0000320 seconds time:15398 詳細(xì)說明: vmop [threads: total initially_running wait_to_block] [time: spin block sync cleanup vmop] page_trap_count
14.610: GenCollectForAllocation [ 13 0 0 ] [ 0 0 0 0 4 ] 0
第一段是時間戳,,VM Operation的類型,以及線程概況
第二段是到達(dá)安全點的各個階段以及執(zhí)行操作所花的時間,其中最重要的是vmop
二,、監(jiān)控點應(yīng)用 如果能在監(jiān)控系統(tǒng)上顯示JVM進(jìn)入安全點的次數(shù)和時間,,那我們排查服務(wù)超時時就可以第一時間先上監(jiān)控系統(tǒng)觀察下當(dāng)時JVM停頓時間的情況。
2.1,、在應(yīng)用里往外吐信息hotspot 里提供里一個API,,但是沒掛到JMX上,所以只適合由泡在應(yīng)用里的代碼往外吐,。
import sun.management.HotspotRuntimeMBean; import sun.management.ManagementFactoryHelper; public class StopTheWorldSout { private static HotspotRuntimeMBean mbean = (HotspotRuntimeMBean) ManagementFactoryHelper.getHotspotRuntimeMBean(); public static void main(String[] args) { long count = mbean.getSafepointCount(); long time = mbean.getTotalSafepointTime(); long syncTime = mbean.getSafepointSyncTime(); } } 三,、示例
/** * 啟動參數(shù):-Xmx512m -Xms512m -XX:+UseSerialGC -XX:+PrintGCDetails -Xmn1m * <p> * -XX:+UseSerialGC:新生代和老年代都用單線程的串行回收器,。適合單核并發(fā)能力差得處理器。 * -XX:+UseParNewGC:新生代用并行的ParNew回收期,,老年代都用單線程的串行回收器,。適合多核,并發(fā)能力強的處理器,。 * -XX:+UseParallelGC:新生代使用ParallelGC回收器,,老年代使用串行回收器。 * -XX:+UseParallelOldGC:新生代使用ParallelGC回收器,,老年代使用ParallelOldGC回收器,。 * 1)-- * -XX:+UseConclMarkSweepGC:老年代使用CMS回收器。 **/ public class StopTheWorldTest { public static class MyThread extends Thread { HashMap<Long, byte[]> map = new HashMap<Long, byte[]>(); @Override public void run() { try { while (true) { if (map.size() * 512 / 1024 / 1024 >= 450) { System.out.println("============準(zhǔn)備清理==========:" + map.size());//大于450M時,,清理內(nèi)存,。 map.clear(); System.out.println("clean map"); } for (int i = 0; i < 100; i++) { map.put(System.nanoTime(), new byte[512]);//消耗內(nèi)存。 } Thread.sleep(1); } } catch (Exception e) { e.printStackTrace(); } } } public static class PrintThread extends Thread { public static final long starttime = System.currentTimeMillis(); @Override public void run() { try { while (true) { long t = System.currentTimeMillis() - starttime; System.out.println("time:" + t); Thread.sleep(100); } } catch (Exception e) { } } } public static void main(String[] args) { MyThread t = new MyThread(); PrintThread p = new PrintThread(); t.start(); p.start(); } } 輸出 time:14548 [GC (Allocation Failure) [DefNew: 959K->64K(960K), 0.0050998 secs] 521756K->521753K(524224K), 0.0051346 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [DefNew: 960K->64K(960K), 0.0048306 secs] 522649K->522646K(524224K), 0.0048594 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 960K->960K(960K), 0.0000189 secs][Tenured: 522582K->523263K(523264K), 0.4516206 secs] 523542K->523538K(524224K), [Metaspace: 3838K->3838K(1056768K)], 0.4516858 secs] [Times: user=0.44 sys=0.01, real=0.46 secs] time:15068 [Full GC (Allocation Failure) [Tenured: 523263K->523263K(523264K), 0.4217772 secs] 524223K->524218K(524224K), [Metaspace: 3838K->3838K(1056768K)], 0.4218278 secs] [Times: user=0.42 sys=0.00, real=0.42 secs] time:15507[Full GC (Allocation Failure) [Tenured: 523263K->523263K(523264K), 0.4132856 secs] 524223K->524223K(524224K), [Metaspace: 3838K->3838K(1056768K)], 0.4133185 secs] [Times: user=0.41 sys=0.00, real=0.42 secs] [Full GC (Allocation Failure) [Tenured: 523263K->515940K(523264K), 0.4557615 secs] 524223K->515940K(524224K), [Metaspace: 3838K->3838K(1056768K)], 0.4557908 secs] [Times: user=0.45 sys=0.01, real=0.45 secs] [GC (Allocation Failure) [DefNew: 896K->63K(960K), 0.0027436 secs] 516836K->516802K(524224K), 0.0027752 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [DefNew: 959K->64K(960K), 0.0048657 secs] 517698K->517697K(524224K), 0.0049047 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 960K->64K(960K), 0.0041075 secs] 518593K->518578K(524224K), 0.0041432 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [DefNew: 960K->63K(960K), 0.0040883 secs] 519474K->519473K(524224K), 0.0041230 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] time:16481 說明:
|
|