這是我在公司給團隊小夥伴一次技術小分享。
新手司機能夠收藏、學習,老司機能夠批評指正。
ps:
內容參考了衆多優秀博文、書籍,部分圖片來源於博文,若有侵權請聯繫刪除。
爲何Java
能夠實現所謂的「一次編寫,處處運行」,主要是由於虛擬機的存在。Java
虛擬機負責Java
程序設計語言的安全特性和平臺無關性。Java虛擬機屏蔽了與具體操做系統平臺相關的信息,使得Java語言編譯器只須要生成在Java
虛擬機上運行的字節碼,就能夠在多種平臺上不加修改地運行。Java虛擬機使得Java擺脫了具體機器的束縛,使跨越不一樣平臺編寫程序成爲了可能。java
Java虛擬機基本上都是JDK
自帶的虛擬機HotSpot
,這款虛擬機也是目前商用虛擬中市場份額最大的一款虛擬機,能夠經過在命令行程序中輸入java -version
來查看:linux
其實市面上還有不少別的優秀的虛擬機。Sun公司除了有大名鼎鼎的HotSpot
外,還有KVM
、Squawk VM
、Maxine VM
,BEA公司有JRockit VM
、IBM公司有J9 VM
等等。算法
Java
虛擬機(JVM
)內部定義了程序在運行時須要使用到的內存區域。內存區域主要分爲主內存和工做內存。主內存即主機物理內存,工做內存按做用域可劃分爲線程獨享區和線程共享區。安全
宏觀來看是這樣子的,以下圖:框架
Java內存模型規定了全部的變量都存儲在主內存(Main Memory
)中,每條線程還有本身的工做內存(Working Memory
),線程的工做內存中保存了被該線程使用到的變量和主內存副本拷貝,線程對變量全部的操做(讀取、賦值)都必須在工做內存中進行,而不能直接讀寫主內存中的變量。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞均須要經過主內存來完成,jvm
Jvm運行時內存模型,不包含主內存。以下圖:ide
下面將會逐一詳細介紹上面的內存區域。函數
Java stack
。聲明週期和線程相同,方法執行會建立棧幀,用於存儲局部變量、操做數棧、動態連接、方法出口等信息。Native method stack
。做用和虛擬機棧同樣,不過面向的是本地方法。不屬於jvm
規範,hotspot
沒有這塊區域。Program counter register
。區域小,是線程執行的字節碼的行號指示器,至關於存的是一條條的指令。用於存放對象實例,是全部內存區域中最大的一塊。實際上這塊內存還被劃分的更細:新生代和老年代,空間佔用比例爲1 : 2
,新生代再細緻一點有:Eden空間、From Survivor(S0)、To Survivor(S1),空間佔用比例爲8 : 1 : 1
。進一步劃分的目的是更好地回收內存,或者更快地分配內存。工具
用於存放虛擬機加載的類信息、常量(常量池)、靜態變量、即便編譯器編譯後的代碼等數據,即「HotSpot」的永久代。在JDK 7以後,咱們使用的HotSpot應該就沒有永久代這個概念,採用的是Native Memory來實現方法區的規劃。佈局
直接內存,即主內存,並非虛擬機運行時數據區的一部分,也不是虛擬機規範中定義的內存區域,可是這部份內存也被頻繁地使用。並且也可能致使OutOfMemoryError異常出現。
JDK1.4中新加入的NIO(New Input/Output)類,能夠直接使用Native函數庫直接分配堆外內存,這樣就能在一些場景中顯著提升性能,由於避免了在Java堆和Native堆之間來回複製數據。
哪些內存須要回收是垃圾回收機制第一個要考慮的問題,所謂「要回收的垃圾」無非就是那些不可能再被任何途徑使用的對象。那麼如何肯定要回收的對象,以及採用什麼樣的策略去回收,適合什麼樣的場景,這是咱們要關注的幾個點。
瞭解一個對象知足什麼樣的條件就認爲是可被回收的對象是重要的一環。
給對象添加一個引用計數器,每當一個地方引用這個對象時,計數器值+1
;當引用失效時,計數器值-1
。當計數值爲0的對象就是不可能再被使用的。這種算法使用場景不少,可是,Java中卻沒有使用這種算法,由於這種算法很難解決對象之間相互引用的狀況。
public class ReferenceCountingGC{ public static void main(String[] args){ ReferenceCountingGC objectA = new ReferenceCountingGC(); ReferenceCountingGC objectB = new ReferenceCountingGC(); objectA.instance = objectB; objectB.instance = objectA; } }
這個算法的基本思想是經過一系列稱爲GC Roots
的對象做爲起始點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈(即GC Roots到對象不可達)時,則證實此對象是不可用的。在Java語言中能夠做爲GC Roots
的對象包括:
Native
方法)引用的對象採用內存空間比例爲1 : 1
的2塊內存上,只使用其中一塊,當須要回收時,將存活的對象複製到另一塊,原有的那一塊內存空間直接所有清除。這種算法比較簡單粗暴,缺點也很明顯,內存只能使用1/2
。
對標識爲可清理的對象直接進行清理操做,不會發生複製或者移動,相對複製算法成本比較小。缺點:對標記的對象清除以後,因爲未移動過對象,將產生大量不連續的內存碎片,當大對象出現時,因爲沒有足夠的連續內存致使不得不對碎片進行整理,也就是Full GC。
標記-整理算法可以解決標記-清除算法帶來的碎片化問題
根據上面提到的回收算法,jvm
內置了擁有衆多的收集器來適應不一樣的場景。根據運行環境的物理配置信息,會自動的選擇使用client模式、server模式的垃圾收集器,還能夠繼續根據運行時數據的狀況來篩選適合當前場景的垃圾收集器。
上圖展現了新生代和老年代的幾種垃圾收集器,其中有連線的表明是能夠組合使用的。
javap -verbose
-X
參數:全部的這類參數都以-X
開始,例如經常使用的-Xmx
,+
或-
,而後才設置JVM
選項的實際名稱。例如,-XX:+
相似true
,表啓用,-XX:-
相似false
。string
或者integer
,咱們先寫參數的名稱,後面加上=
,最後賦值。例如 -XX:ParamName
=Value
-Xms
即 -XX:InitialHeapSize
的縮寫,指定JVM的初始內存大小
-Xms20M 設置JVM啓動內存的最小值爲20M,必須以M爲單位
-Xmx
即 -XX:MaxHeapSize
的縮寫,指定JVM的最大堆內存大小
-Xmx20M 表示設置JVM啓動內存的最大值爲20M,單位爲M,將-Xmx和-Xms設置爲同樣能夠避免JVM內存自動擴展。
-verbose:gc
輸出虛擬機中GC的詳細狀況-Xss128k
設置虛擬機棧的大小爲128k-Xoss128k
設置本地方法棧的大小爲128k。HotSpot不區分虛擬機棧和本地方法棧,所以對於HotSpot這個參數無效。-XX:PermSize=10M
JVM初始分配的永久代的容量,必須以M爲單位-XX:MaxPermSize=10M
JVM容許分配的永久代的最大容量,必須以M爲單位,大部分狀況下這個參數默認爲64M-Xnoclassgc
關閉JVM對類的垃圾回收-XX:+TraceClassLoading
查看類的加載信息-XX:+TraceClassUnLoading
查看類的卸載信息-XX:NewRatio=4
設置年輕代:老年代的大小比值爲1:4,這意味着年輕代佔整個堆的1/5-XX:SurvivorRatio=8
設置2個Survivor區:1個Eden區的大小比值爲2:8,這意味着Survivor區佔整個年輕代的1/5,這個參數默認爲8-Xmn20M
設置年輕代的大小爲20M-XX:+HeapDumpOnOutOfMemoryError
可讓虛擬機在出現內存溢出異常時Dump出當前的堆內存轉儲快照-XX:+UseG1GC
讓JVM使用G1垃圾收集器-XX:+PrintGCDetails
在控制檯上打印出GC具體細節-XX:+PrintGC
在控制檯上打印出GC信息-XX:PretenureSizeThreshold=3145728
對象大於3145728(3M)時直接進入老年代分配,單位爲byte-XX:MaxTenuringThreshold=1
對象年齡大於1,自動進入老年代-XX:CompileThreshold=1000
一個方法被調用1000次以後,會被認爲是熱點代碼,並觸發即時編譯-XX:+PrintHeapAtGC
能夠看到每次GC先後堆內存佈局-XX:+PrintTLAB
能夠看到TLAB的使用狀況-XX:+UseSpining
開啓自旋鎖-XX:PreBlockSpin
更改自旋鎖的自旋次數,使用這個參數必須先開啓自旋鎖命令行
java -jar projectName.jar -verbose:gc -Xms20M -Xmx20M
所謂工具,就是經過一些簡便的腳本去執行程序去呈現結果數據。這裏涉及到一些語法格式。
統一語法都相似這種形式:$ cmd [option id[ pid | vmid |hostid ]]
其中hostid
爲可選項,默認爲localgost
, vmid
/pid
依賴jps
獲取
jps
是Java Process Status
的縮寫,查看當前java
進程的運行狀態快照。理解爲linux
命令ps
的java
版本
-m
運行時傳入的參數-v
虛擬機參數-l
運行的主類全限定名或jar
包名稱示例
jps -mlv
jstat
是JVM Statistics Monitoring Tool
的縮寫,查看虛擬機統計信息監控數據,如類信息、內存、垃圾收集、JIT
編譯等
-gc
顯示gc的信息,查看gc
次數以及時間-class
監視類裝載、卸載數量、總空間以及類裝載所耗費的時間-gc
監視Java堆情況,包括Eden
區、兩個Survivor
區、老年代、永久帶等的容量、已用空間、GC
時間合計等信息-gccapacity
監視內容基本與-gc
相同,但輸出主要關注Java堆各個區域使用到的最大、最小空間-gcutil
監視內容基本與-gc
相同,但輸出主要關注已使用的空間佔總空間的百分比-gccause
與-gcutil
功能同樣,可是會額外輸出致使上一次GC
產生的緣由-gcnew
監視新生代GC
情況-gcnewcapacity
監視內容基本與-gcnew
相同,但輸出主要關注使用到的最大、最小空間-gcold
監視老年代GC
情況-gcoldcapacity
監視內容基本與-gcold
相同,但輸出主要關注使用到的最大、最小空間-gcpermcapacity
輸出永久代使用到的最大、最小空間-compiler
輸出JIT編譯器編譯過的方法、耗時等信息-printcompilation
輸出已經被JIT
編譯的方法jstat -gcutil pid
依賴jps
得到pid
查看類裝載、內存、垃圾收集、jit
編譯信息示例
jstat -gcutil 3333 1000 10
對pid
爲3333
的進程每隔1
秒打印1
次,總打印10
次jinfo
即Configuration Info for Java
,實時查看和調整jvm
參數
-flag <name>
打印jvm參數的值-flag [+|-]<name>
啓用/禁用jvm參數-flag <name>=<value>
修改jvm參數值-flags <pid>
打印全部jvm參數值-sysprops <pid>
打印java系統屬性<no option> <pid>
打印上面全部信息示例
jmap
即Memory Map for Java
,內存映像工具用於生成堆轉存快照
-dump
生成Java堆轉儲快照。格式爲-dump:[live, ]format=b,file=<filename>
,其中live
自參數說明是否只dump
出存活的對象-finalizerinfo
顯示在F-Queue
中等待Finalizer
線程執行finalize
方法的對象。只在Linux
和Solaris
系統有效-heap
顯示Java
堆詳細信息,如使用哪一種收集器、參數配置、分代情況等。只在Linux
和Solaris
系統有效-histo
顯示堆中對象統計信息,包括類、實例數量、合計容量-permstat
以ClassLoader
爲統計口徑顯示永久代內存狀態。只在Linux
和Solaris
系統下有效-F
當虛擬機進行對-dump
選項沒有響應時,可以使用這個選項強制生成dump
快照。只在Linux
和Solaris
系統下有效示例
jmap -dump:live,format=b,file=heap.bin 7298
將pid
爲7298
的虛擬機內活對象導出爲heap.bin
二進制文件jhat
即JVM Heap Analysis Tool
,虛擬機堆分析工具
示例
jhat /data/dump.bin
分析導出的堆快照jstack
即Stack Trace for Java
, 堆棧跟蹤工具,查看虛擬機線程快照。目的主要是定位線程長時間出現停頓的緣由,如線程間死鎖、死循環、請求外部資源致使的長時間等待等都是致使線程長時間停頓的緣由。
-F
即force
,強制打印線程快照信息-m
即mixed mode
,同時打印java
框架信息和本地庫信息-l
即long listing
,打印更長(更多)的列信息示例
jstack -F 7298
jstack -l 7298
jstack -m 7298