Java程序員進階必備 - JVM快速入門

這是我在公司給團隊小夥伴一次技術小分享。
新手司機能夠收藏、學習,老司機能夠批評指正。
ps:內容參考了衆多優秀博文、書籍,部分圖片來源於博文,若有侵權請聯繫刪除。

1. 前言

爲何Java能夠實現所謂的「一次編寫,處處運行」,主要是由於虛擬機的存在。Java虛擬機負責Java程序設計語言的安全特性和平臺無關性。Java虛擬機屏蔽了與具體操做系統平臺相關的信息,使得Java語言編譯器只須要生成在Java虛擬機上運行的字節碼,就能夠在多種平臺上不加修改地運行。Java虛擬機使得Java擺脫了具體機器的束縛,使跨越不一樣平臺編寫程序成爲了可能。java

Java虛擬機基本上都是JDK自帶的虛擬機HotSpot,這款虛擬機也是目前商用虛擬中市場份額最大的一款虛擬機,能夠經過在命令行程序中輸入java -version來查看:linux

java-version

其實市面上還有不少別的優秀的虛擬機。Sun公司除了有大名鼎鼎的HotSpot外,還有KVMSquawk VMMaxine VM,BEA公司有JRockit VM、IBM公司有J9 VM等等。算法

2. 內存模型(JMM)

Java虛擬機(JVM)內部定義了程序在運行時須要使用到的內存區域。內存區域主要分爲主內存和工做內存。主內存即主機物理內存,工做內存按做用域可劃分爲線程獨享區和線程共享區。安全

宏觀來看是這樣子的,以下圖:框架

asss

Java內存模型規定了全部的變量都存儲在主內存(Main Memory)中,每條線程還有本身的工做內存(Working Memory),線程的工做內存中保存了被該線程使用到的變量和主內存副本拷貝,線程對變量全部的操做(讀取、賦值)都必須在工做內存中進行,而不能直接讀寫主內存中的變量。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞均須要經過主內存來完成,jvm

Jvm運行時內存模型,不包含主內存。以下圖:ide

下面將會逐一詳細介紹上面的內存區域。函數

2.1 線程獨享區

  • 虛擬機棧,即Java stack。聲明週期和線程相同,方法執行會建立棧幀,用於存儲局部變量、操做數棧、動態連接、方法出口等信息。
  • 本地方法棧,即Native method stack。做用和虛擬機棧同樣,不過面向的是本地方法。不屬於jvm規範,hotspot沒有這塊區域。
  • 程序計數器,即Program counter register。區域小,是線程執行的字節碼的行號指示器,至關於存的是一條條的指令。

2.2 線程共享區

  • 用於存放對象實例,是全部內存區域中最大的一塊。實際上這塊內存還被劃分的更細:新生代和老年代,空間佔用比例爲1 : 2,新生代再細緻一點有:Eden空間、From Survivor(S0)、To Survivor(S1),空間佔用比例爲8 : 1 : 1。進一步劃分的目的是更好地回收內存,或者更快地分配內存。工具

  • 方法區

    用於存放虛擬機加載的類信息、常量(常量池)、靜態變量、即便編譯器編譯後的代碼等數據,即「HotSpot」的永久代。在JDK 7以後,咱們使用的HotSpot應該就沒有永久代這個概念,採用的是Native Memory來實現方法區的規劃。佈局

2.3 直接內存

直接內存,即主內存,並非虛擬機運行時數據區的一部分,也不是虛擬機規範中定義的內存區域,可是這部份內存也被頻繁地使用。並且也可能致使OutOfMemoryError異常出現。

JDK1.4中新加入的NIO(New Input/Output)類,能夠直接使用Native函數庫直接分配堆外內存,這樣就能在一些場景中顯著提升性能,由於避免了在Java堆和Native堆之間來回複製數據。

3. 垃圾回收(GC)

哪些內存須要回收是垃圾回收機制第一個要考慮的問題,所謂「要回收的垃圾」無非就是那些不可能再被任何途徑使用的對象。那麼如何肯定要回收的對象,以及採用什麼樣的策略去回收,適合什麼樣的場景,這是咱們要關注的幾個點。

3.1 肯定對象算法

瞭解一個對象知足什麼樣的條件就認爲是可被回收的對象是重要的一環。

3.1.1 引用計數法

給對象添加一個引用計數器,每當一個地方引用這個對象時,計數器值+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;
    }
}
3.1.2 可達性分析法

這個算法的基本思想是經過一系列稱爲GC Roots的對象做爲起始點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈(即GC Roots到對象不可達)時,則證實此對象是不可用的。在Java語言中能夠做爲GC Roots的對象包括:

  • 虛擬機棧中引用的對象
  • 方法區中靜態屬性引用的對象
  • 方法區中常量引用的對象
  • 本地方法棧中JNI(即Native方法)引用的對象

3.2 回收算法

3.2.1 複製算法

採用內存空間比例爲1 : 1的2塊內存上,只使用其中一塊,當須要回收時,將存活的對象複製到另一塊,原有的那一塊內存空間直接所有清除。這種算法比較簡單粗暴,缺點也很明顯,內存只能使用1/2

複製算法

3.2.2 標記-清除算法

對標識爲可清理的對象直接進行清理操做,不會發生複製或者移動,相對複製算法成本比較小。缺點:對標記的對象清除以後,因爲未移動過對象,將產生大量不連續的內存碎片,當大對象出現時,因爲沒有足夠的連續內存致使不得不對碎片進行整理,也就是Full GC。

標記-清除算法

3.2.3 標記-整理算法

標記-整理算法可以解決標記-清除算法帶來的碎片化問題

標記-整理算法

3.3 垃圾收集器

根據上面提到的回收算法,jvm內置了擁有衆多的收集器來適應不一樣的場景。根據運行環境的物理配置信息,會自動的選擇使用client模式、server模式的垃圾收集器,還能夠繼續根據運行時數據的狀況來篩選適合當前場景的垃圾收集器。

垃圾收集器圖

上圖展現了新生代和老年代的幾種垃圾收集器,其中有連線的表明是能夠組合使用的。

4. jvm參數

4.1 參數規則

  • 標準參數:例如 javap -verbose
  • -X參數:全部的這類參數都以-X開始,例如經常使用的-Xmx,
  • 布爾類型的參數: +-,而後才設置JVM選項的實際名稱。例如,-XX:+相似true,表啓用,-XX:-相似false
  • 非布爾值的參數:如string或者integer,咱們先寫參數的名稱,後面加上=,最後賦值。例如 -XX:ParamName=Value

4.2 經常使用參數清單

  • -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 更改自旋鎖的自旋次數,使用這個參數必須先開啓自旋鎖

4.3 使用參數

  • 命令行

    java -jar projectName.jar -verbose:gc -Xms20M -Xmx20M
  • Eclipse

    Eclipse

  • IDEA

    idea

5. 經常使用工具

所謂工具,就是經過一些簡便的腳本去執行程序去呈現結果數據。這裏涉及到一些語法格式。

統一語法都相似這種形式:$ cmd [option id[ pid | vmid |hostid ]]

其中hostid爲可選項,默認爲localgost, vmid/pid依賴jps獲取

5.1 jps

jpsJava Process Status的縮寫,查看當前java進程的運行狀態快照。理解爲linux命令psjava版本

  • -m 運行時傳入的參數
  • -v 虛擬機參數
  • -l 運行的主類全限定名或jar包名稱
  • 示例

    • jps -mlv

5.2 jstat

jstatJVM 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 10pid3333的進程每隔1秒打印1次,總打印10

5.3 jinfo

jinfoConfiguration Info for Java,實時查看和調整jvm參數

  • -flag <name> 打印jvm參數的值
  • -flag [+|-]<name> 啓用/禁用jvm參數
  • -flag <name>=<value> 修改jvm參數值
  • -flags <pid> 打印全部jvm參數值
  • -sysprops <pid> 打印java系統屬性
  • <no option> <pid> 打印上面全部信息
  • 示例

    • jinfo -flags 7298 打印pid爲7298的虛擬機運行時的全部參數
    • xxx

5.4 jmap

jmapMemory Map for Java,內存映像工具用於生成堆轉存快照

  • -dump 生成Java堆轉儲快照。格式爲-dump:[live, ]format=b,file=<filename>,其中live自參數說明是否只dump出存活的對象
  • -finalizerinfo 顯示在F-Queue中等待Finalizer線程執行finalize方法的對象。只在LinuxSolaris系統有效
  • -heap 顯示Java堆詳細信息,如使用哪一種收集器、參數配置、分代情況等。只在LinuxSolaris系統有效
  • -histo 顯示堆中對象統計信息,包括類、實例數量、合計容量
  • -permstatClassLoader爲統計口徑顯示永久代內存狀態。只在LinuxSolaris系統下有效
  • -F 當虛擬機進行對-dump選項沒有響應時,可以使用這個選項強制生成dump快照。只在LinuxSolaris系統下有效
  • 示例

    • jmap -dump:live,format=b,file=heap.bin 7298pid7298的虛擬機內活對象導出爲heap.bin二進制文件

5.5 jhat

jhatJVM Heap Analysis Tool,虛擬機堆分析工具

  • xxx
  • 示例

    • jhat /data/dump.bin 分析導出的堆快照

5.6 jstack

jstackStack Trace for Java, 堆棧跟蹤工具,查看虛擬機線程快照。目的主要是定位線程長時間出現停頓的緣由,如線程間死鎖、死循環、請求外部資源致使的長時間等待等都是致使線程長時間停頓的緣由。

  • -Fforce,強制打印線程快照信息
  • -mmixed mode,同時打印java框架信息和本地庫信息
  • -llong listing,打印更長(更多)的列信息
  • 示例

    • jstack -F 7298
    • jstack -l 7298
    • jstack -m 7298
相關文章
相關標籤/搜索