認識JVM性能監控與故障處理工具&深入理解Java內存模型

offer 105 

先來複習:

1. 內存區,jvm的內存區,java語言的內存調試工具,jdk bin目錄下的工具。

以下從《深入理解Java虛擬機》獲取

1. 

名稱 主要作用
jps JVM Process Status Tool, 顯示指定系統內所有的HotSpot虛擬機進程
jstat JVM Statistics Monitoring Tool , 用於收集HotSpot虛擬機各方面的運行數據
jinfo Configuration Info for Java,顯示虛擬機配置信息
Jmap Memory Map for Java ,生成虛擬機的內存轉儲快照(heapdump文件)
jhat JVM Heap Dump Browser,用於分析heapdump文件,它會建立一個HTTP/HTML服務器,讓用戶在瀏覽器上看到分析結果。
jstack Stack Trace for Java, 顯示虛擬機的線程快照

jps命令格式:
    jps [ options ] [ hostid ]
    jps可以通過RMI協議查詢開啓了RMI服務的遠程虛擬機進程狀態,hostid爲RMI註冊表中註冊的主機名。
    jps工具主要選項
    (1)-q : 只輸出LVMID,省略主類的名稱
    (2)-m :輸出虛擬機進程啓動時傳遞給主類main()函數的參數
    (3)-l :輸出主類的全名,如果進程執行的是Jar包,輸出Jar路徑
    (4)-v:輸出虛擬機進程啓動時JVM參數
執行結果如下:


運行代碼:

import java.util.*;
public class template{
	public static void main(String[] args){
		test();
		if(args[0]==null){
			System.out.println("it is null");
		}
		else{
			System.out.println(args[0]);
		}

		while(true){
			new temp();
		}
	}

	static void test()
	{
		String a = "hEllo,world";

		int c = a.codePointCount(1,6);

		

		char[] b = a.toCharArray();

		byte[] d = a.getBytes();
		System.out.println(Arrays.toString(d));
		System.out.println(c);
		

	}


}

class temp{
	temp(){System.out.println("hello, I am temp");}
}

在控制檯輸入:java template kingpin : 結果自然是一隻輸出:


然後在運行 jps -q  -m -l :




2. jstat:虛擬機統計信息監視工具

然後讓上述代碼繼續運行,用jstat查看結果:

jstat -gc 5096 250 5


分析一下:(單位爲KB)

S0C:Survivor0 的容量, capacity

S1C:Survivor1, 與S0C一樣

S0U: Survivor0 的利用情況,utilization

S1U: 

EC:eden 新生代使用情況

EU:

OC,OU:老年代的一些情況

PC,PU:Permanent space 當前永久代的情況

YGC: Numbers of young generation GC events

YGCT: Young generation Garbage collection time

FGC : full gc 次數

FGCT: full gc 時間

GCT: total gc 時間

由上圖可以看出其實這個程序運行了這麼久一直增加的只有EU , 在筆者寫博文的時候程序還是一直在運行,現在再查看一下情況:


這個例子就比較好點,因爲沒1000 毫秒才查詢一次gc的情況:

在最後的此一次查詢中 65 次 gc 執行了, 並且使用了 0.117 秒,個人估計的話這個時間是累積的。

請參見博文:jstat全解


PS: 補充Young generation 中 Eden代和 Survivor 代的主要區別:

大多數情況下,對象在新生代 Eden區中分配,當Eden區沒有足夠的空間進行分配時,虛擬機將發起一次minor gc, 就是新生代的 垃圾收集

-XX:SurvivorRatio =8 決定了YG中Eden分區與一個Survivor 去的空間比例是 8:1,

對象的運行原則是 Eden 與 Survivor ,先放Eden ,再放Survivor 如果 Eden中沒有空間,發起一次發起一次 Minor Gc , 還是沒有空間,放入 Survivor , 還是沒有(比如說大對象),分配擔保機制提前轉移到老年代。

如果對象在Eden出生並經過第一次Minor gc後仍然存活,並且能被Survivor 容納的話,將被移動到Survivor空間中,並將對象年齡設爲1, 在Survivor中每熬過一次 Minor gc, 年齡每增加一歲,默認增加到 15歲的時候會升到老年代。 

下面先試一試 -XX:SurvivorRatio=7(注意無空格) 的運行:


可以看到:


這個時候 EC 已經是 S0C 和 S1C的7倍。


3. jinfo:Java配置信息工具



4. Jmap:Java內存影像工具

又是在windows平臺下受限制的一個工具


-histo  顯示堆中對象統計信息,包括類,實例數量和合計容量等。


4. jhat:虛擬機堆轉儲快照分析工具

分析功能相對簡陋,有更專業的例如:專業用於分析dump文件的Eclipse Memory Analyzer, IBM HeapAnalyzer等

不予展示


5. jstack : Java 堆棧跟蹤工具


選項 作用
-F 當正常輸出的請求不被響應時,強制輸出線程堆棧
-l 除堆棧外,顯示關於鎖的附加信息
-m 如果調用奧本地方法的話,可以顯示C/C++的堆棧

可視化工具:

1. JConsole:

在bin目錄下 啓動 JConsole.exe 

很多功能都還沒時間看


2. VisualVM:多合一故障處理工具


還需要安裝很多的插件VisualVM的功能才能完善。

重看 《深入理解Java虛擬機》一書,內容多得驚人,以前的學習是多麼水。



2. 深入理解Java內存模型的全面複習和總結與回顧:

深入理解Java內存模型(程曉明),程曉明就職於富士通南大,日本第一次IT企業。

深入理解Java內存模型(一)基礎

併發編程中兩大問題:線程之間如何通信及線程之間如何同步。

通信是指線程之間以何種機制來交換信息。命令式編程中,通信機制有兩種:共享內存,消息傳遞。

同步是指程序控制不同線程之間操作發生相對順序的機制,在共享內存併發模型,同步顯示進行。消息傳遞的併發模型中,同步是隱式的,應爲消息的發送在接受之前。

Java的併發採用的是共享內存模型。這就要求程序員瞭解隱式進行的線程之間通信的工作機制。


實例域,靜態域,數組元素存儲在堆內存中,堆內存在線程之間共享。

局部變量,方法定義參數,異常處理器參數,不會再內存之間共享,不會有內存可見性問題。

模擬一個線程本地內存(本地內存是JMM的一個抽象概念,並不真實存在,它涵蓋了緩存,寫緩衝區,寄存器以及其他的硬件和編譯器優化)

線程A和線程B之間要通信:

1. 線程A把本地內存A中更新過的共享變量刷新到主內存。

2. 然後,線程B到主內存中去讀取線程A已經更新過的共享變量。


重排序:

1.編譯器,編譯器在不改變但線程程序語義的前提下,重新安排語句的執行。

2.指令級並行,不存在數據依賴,處理器可以改變語句對應機器指令的執行順序。

3.內存系統,緩存和讀/寫緩衝區。



對於編譯器重排序(1),JMM禁止特定類型的重排。

對於處理器重排(2和3),JMM要求java編譯器在生成指令序列時,插入特定類型的內存屏障(memory barriers,intel : memory fence)

JMM屬於語言級的內存模型。


由於寫緩衝區僅對自己的處理器可見。現代處理器都會使用寫緩衝區,因此現代的處理器都會允許對寫讀操作重排。


內存屏障:


StoreLoad Barriers 是一個“全能型”的屏障,它同時具有其他三個屏障的效果,執行該屏障開銷會很昂貴,因爲當前處理器要把寫緩衝區中的數據全部刷新到內存中。

JSP-133內存模型,happens-before



深入理解Java內存模型(二):重排序

請注意:不同處理器之間和不同線程之間的數據依賴性不被編譯器和處理器考慮

as-if-serial語義:

不管怎麼重排序,(單線程)程序的執行結果不能被改變。(但是確實允許重排序)

在單線程程序中,對控制依賴的重排序是不會改變執行結果(同時也允許),as-if-serial語義也允許,但是在多線程中,對控制依賴的操作重排序,可能會改變程序執行結果。

對數據依賴的重排序就無論如何不允許了。


深入理解Java內存模型(三):順序一致性

如果程序是正確同步的,程序的執行將具有順序一致性(sequentially consistent),程序的執行結果與該程序在順序一致性模型中的執行結果相同。同步原語(lock,volatile,final)

對於未同步或未正確同步的多線程程序,JMM只提供最小安全性:線程執行時讀取到的值,要麼是之前某個線程寫入的值,要麼是默認值(0,null,false).

JMM不保證對64位的long型和double型變量的讀/寫操作具有原子性,而,順序一致性模型保證對所有的內存讀/寫操作都具有原子性。

在這篇博文裏,詳細說明了總線仲裁,和在一些32位處理器上,java語言規範鼓勵但不強求JVM對64位long型變量和double型變量的讀/寫具有原子性。

然後把一個64位long/double型變量的讀/寫操作拆分爲兩個32位的讀/寫操作。可能在不同的總線事務中執行,此時對這個64位變量的讀/寫操作將不具有原子性。


深入理解Java內存模型(四):volatile

對一個volatile變量的讀,總是能看到(任意線程)對這個volatile變量最後的寫入。

JMM針對編譯器制定的volatile重排序規則表


x86中,JMM僅需要在volatile寫後面插入一個StoreLoad屏障即可正確實現volatile寫-讀的內存語義。此話要結合x86的允許的重排類型以及volatile的內存語義理解。

JSP-133專家組決定增強volatile的內存語義:嚴格限制編譯器和處理器對volatile變量與普通變量的重排序,確保volatile的寫-讀和監視器的釋放-獲取一樣,具有相同的內存語義。與監視器所比較,volatile在可伸縮性方面有優勢?怎麼理解這個可伸縮性?


深入理解Java內存模型(五)-鎖

記憶:鎖的釋放-獲取內存語義和volatile對象的寫-讀操作有相同的內存語義,

公平鎖在釋放-獲取上和volatile的寫-讀有相同的內存語義

非公平鎖在釋放上和公平鎖一樣,但是獲取上卻使用了CAS來實現與volatile讀寫的內存語義,CAS,是compareAndSet

CAS同時具有volatile讀和volatile寫的內存語義,查實這一段我是萬萬看不懂。。。汗。。。

博文通過對ReentrantLock的分析,鎖釋放-獲取的內存語義的實現至少有:

1. 利用volatile變量的寫-讀所具有的內存語義。

2. 利用CAS所附帶的volatile讀和volatile寫的內存語義。


這兩個可以整個concurrent包實現的基石中的基石。


深入理解Java內存模型(六)-final

final域的重排序限制:


寫final域的重排序規則可以確保:在對象引用爲任意線程可見之前,對象的final域已經被正確初始化過了,而普通域不具有這個保障。

final引用在構造器構造前,與把這個引用賦值給一個引用變量,兩操作之間不能重排序。

final引用不能從構造函數內“逸出”(個應該是要由程序員來保證)

JSR-133專家組:

只要對象是正確構造的(被構造對象的引用在構造函數中沒有“逸出”),那麼不需要使用同步(指lock和volatile的使用),就可以保證任意線程都能看到這個final域在構造函數中被初始化之後的值。


深入理解Java內存模型(七)-總結

常見處理器的內存模型:

1. total store ordering TSO

2. partial store order PSO

3. relaxed memory order RMO 和 PowerPC內存模型

JSR-133對JDK5之前的舊內存模型的修補主要有兩個:

  • 增強volatile的內存語義。舊內存模型允許volatile變量與普通變量重排序。JSR-133嚴格限制volatile變量與普通變量的重排序,使volatile的寫-讀和鎖的釋放-獲取具有相同的內存語義。
  • 增強final的內存語義。在舊內存模型中,多次讀取同一個final變量的值可能會不相同。爲此,JSR-133爲final增加了兩個重排序規則。現在,final具有了初始化安全性。
好了,終於暫時把Java內存模型的東西回顧了一遍。加油吧騷年。



3. 有點高大上的 Facebook Flux

4. 明天晚上的有米科技的 python框架:Django







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章