一份關於jvm內存調優及原理的學習筆記

一.虛擬機的基本結構

 

1.jvm整體架構

 

 

類加載子系統:負責從文件系統或者網絡中加載class信息,存入方法區中。

方法區(Perm):存放加載後的class信息,包括靜態方法,jdk1.6以前包含了常量池。

參數:-XX:PermSize初始值  -XX:MaxPermSize最大值

 

Java堆(Heap):java工程的主要內存工作區域,所有線程共享,jdk1.7以後包含了常量池。參數: -Xms初始值     -Xmx最大值

 

直接內存:java堆外,直接向系統申請的內存區間,允許NIO庫使用。申請空間慢,讀寫快。默認下最大可用空間等於堆的最大可用空間。在server模式下,讀寫速度是堆的10倍。

參數:-XX:MaxDirectMemorySize 最大值

 

垃圾回收器:

 

Java棧:線程私有,用於存放局部變量,方法參數,同時和java方法的調用返回密切相關。

參數:-Xss最大值

 

本地方法棧:和java棧類似,主要用於本地方法調用。

PC寄存器:線程私有

執行引擎:

 

 

Java [-options] class [args...]

其中-options是java虛擬機的啓動參數,args是傳遞給main方法的參數、

 

 

2.java堆

根據垃圾回收機制的不同,java堆有可能擁有不同的結構,常見的java堆分爲新生代和老年代。其中新生代存放剛創建的對象及年齡不大的對象,老年帶存放着在新生代中經歷過多次回收後還存在的對象。

 

 

 

對象晉升過程:

新生代分爲eden區s0,s1區(from,to)。多數情況下對象首先分配在eden區,在一次新生代回收後,存活下來的對象存入s0或s1區。每經過一次新生代的回收,對象的年齡加1。默認情況下年齡達到15的對象將晉升至老年代。如果在第一次回收的時候,存活的對象大於s0(s1)空間,將直接晉升至老年代,如果在爲對象第一次分配空間時,對象空間大於eden空間的話,對象也直接分配到老年代。

 

 

3.java棧

Java棧和數據結構中的棧有着類似的含義,先進後出,只支持入棧和出棧操作。Java棧中保存的只要內容是棧幀,每一次進行函數調用,都會有一個對應的棧幀被壓入棧中,函數調用結束,都會有一個棧幀被彈出棧。

 

 

棧幀

每一個棧幀中包含局部變量表,操作數棧和幀數據區。

棧上分配

棧上分配的基本思想,是將線程私有的對象,打散分配到棧上,分配在棧上的函數調用結束後對象會自行銷燬,不需要垃圾回收接入,從而提升性能。對於大量的零散小對象,棧上分配提供了一種很好的對象分配優化策略,但由於和堆空間相比,棧空間較小,因此大對象無法也不適合在棧上分配

棧上分配依賴逃逸分析和標量替換的實現,同時必須在server模式下才能啓用。參數-XX:+DoEscapeAnalysis啓用逃逸分析     -XX:+EliminateAllocations開啓標量替換(默認打開).

例:-server -Xms 100m -Xmx 100m -XX:+DoEscapeAnalysis -XX:+EliminateAllocations

 

 

二.Jvm常用參數

 

1.GC參數

-XX:+PrintGC    每次觸發GC的時候打印相關日誌

-XX:+PrintGCDetails    更詳細的GC日誌

-XX:+PrintHeapAtGC    每次GC時打印堆的詳細詳細信息

-XX:+PrintGCApplicationConcurrentTime    打印應用程序執行時間

-XX:+PrintGCApplicationAtoppedTime    打印應用程序由GC引起的停頓時間

-XX:+PrintReferenceGC    跟蹤系統內的軟引用,弱引用,虛引用和finallize隊列。

 

 

1.類跟蹤

-verbose:class    跟蹤類的加載和卸載

-XX:+TraceClassLoading    單獨跟蹤類加載

-XX:+TraceClassUnloading    單獨跟蹤類卸載

-XX:+PrintClassHistogram    查看運行時類的分佈情況,使用時在控制檯按ctrl+break

 

 

2.系統參數查看

-XX:+PrintVMOptions       運行時,打印jvm接受的命令行顯式參數

-XX:+PrintCommandLineFlags    打印傳遞jvm的顯式和隱式參數

-XX:+PrintFlagsFinal    打印所有系統參數值

 

 

3.堆

-Xms    堆初始值

-Xmx    堆最大可用值

-Xmn    新生代大小,一般設爲整個堆的1/3到1/4左右

-XX:SurvivorRatio    設置新生代中eden區和from/to空間的比例關係n/1

-XX:NewRatio    設置老年代與新生代的比

 

想要合理的分配堆內存,需要了解對象的晉升過程,可以參照上面介紹堆空間架構時,對對象晉升過程的描述。

 

 

基本策略:堆的不同分佈情況,對系統會產生一定的影響。儘可能將對象預留在新生代,減少老年代GC的次數(通常老年回收起來比較慢)。實際工作中,通常將堆的初始值和最大值設置相等,這樣可以減少程序運行時進行的垃圾回收次數和空間擴展,從而提高程序性能。

 

4.非堆

-XX:PermSize   方法區(永久區)初始值

-XX:MaxPermSize    方法區(永久區)最大值

-Xss    設置棧空間大小

-XX:MaxDirectMemorySize    直接內存最大可用空間,設置不當可能導致系統OOM

 

5.虛擬機工作模式

-client    默認工作模式

-server    server工作模式,啓動虛擬機時需要顯式指定

與client模式相比,server模式啓動較慢,會嘗試蒐集更多的系統性能信息,使用更復雜的優化算法對程序進行優化,server模式下系統完全啓動並進入穩定期後,執行速度遠遠快於client模式,適合長期後臺運行的系統。Client模式更適合運行時間不長,又追求啓動速度的客戶端程序。

 

 

三.Jvm性能監控工具

1.JConsole

內存監控,線程監控,類加載情況,虛擬機信息

2.Visual VM

線程dump和分析,性能分析,內存快照分析,BTrace

3.Mission Control

MBean服務器,飛行記錄器

 

四.分析java堆

1.常見的內存溢出原因及解決思路

(1)堆溢出:設置-Xmx調整最大可用堆空間

 

(2)直接內存溢出:可能是系統內存空間不足,同時沒達到參數默認的上限,沒有觸發GC導致OOM,解決方法是通過-XX:MaxDirectMemorySize 來限制最大內存。

 

(3)過多線程導致OOM:由於每開啓一個線程都會給這個線程分配一個棧,因此當線程數達到一定程度,系統空間不足的時候就會內存溢出,可以嘗試減少堆空間,或者可以通過設置參數-Xss限制每個棧的大小。

 

(4)永久區溢出:系統加載的類過多,導致永久區溢出,通過-XX:MaxPermSize來設置永久區最大可用空間。

 

(5)GC效率低下引起的OOM:GC是內存回收的關鍵,回收效率低很有可能引起內存溢出,可以通過合理的分配堆(包括新生代和老年代)空間去解決。

 

 

2.String造成的內存泄漏

內存泄漏是指,不再使用的對象佔據內存不釋放,導致可用內存不斷減小,最終引起內存泄漏。在Java1.6中String.subString()方法就存在這樣的問題。

SubString中新生成的對象並沒有從value中獲取自己需要的那部分,而是直接簡單的使用了相同的引用,只是修改了offset和count,以此來確定新的String對象的值。當原始字符串還在用的時候這種情況是沒有問題的,並且共用value還節省了部分的空間,但是一旦原始字符串被回收,value中多餘的部分就造成了空間浪費。

 

3.淺堆和深堆

淺堆:是指一個對象本身所消耗的內存,不包括其內部引用的對象的大小。

深堆:是指對象的保留集中所有對象淺堆的大小之和。

保留集:是指當對象A被垃圾回收後,可以釋放的所有對象的集合(包括A本身),通俗的講就是,僅被對象A所持有的對象的集合。

 

4.OQL查詢語句

類似於sql語法的查詢語句,可以在堆中進行對象的查找和篩選。


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