JVM筆記——技術點彙總

目錄

· 初步認識html

    · Java里程碑(關鍵部分)java

    · 理解虛擬機ios

    · Java虛擬機種類算法

    · Java語言規範shell

    · Java虛擬機規範編程

· 基本結構數組

    · Java堆(Heap)緩存

    · Java棧(Stacks)安全

    · 方法區(Method Area)數據結構

    · 直接內存(Direct Memory)

    · 本地方法棧(Native Method Stacks)

· 經常使用參數

    · 設置參數

    · 查看參數

    · 跟蹤垃圾回收

    · 跟蹤類加載/卸載

    · 設置初始堆和最大堆

    · 設置堆分佈

    · 處理堆溢出

    · 配置方法區

    · 配置棧

    · 配置直接內存

    · 配置工做模式

· 垃圾回收算法

    · 垃圾回收

    · 引用計數法(Reference Counting)

    · 標記清除法(Mark-Sweep)

    · 複製算法(Coping)

    · 標記壓縮法(Mark-Compact)

    · 分代算法(Generational Collecting)

    · 分區算法(Region)

    · 判斷可觸及性

    · 停頓現象

· 垃圾回收器

    · 串行回收器

    · 新生代ParNew回收器

    · 新生代ParallelGC回收器

    · 老年代ParallelOldGC回收器

    · CMS回收器

    · G1回收器

· 垃圾回收其餘細節

    · 禁用System.gc()

    · 開啓System.gc()併發回收

    · 對象什麼時候進入老年代

    · TLAB

· 性能監控工具

    · top命令

    · vmstat命令

    · iostat命令

    · pidstat命令

    · jps命令

    · jstat命令

    · jinfo命令

    · jmap命令

    · jhat命令

    · jstack命令

    · jcmd命令

    · Visual VM工具

    · Mission Control工具

· 解決OOM問題

    · 堆溢出

    · 直接內存溢出

    · 過多線程致使溢出

    · 永久區/元數據區溢出

    · GC效率低致使OOM

· String實現細節

    · String對象特色

    · String常量池

· 字節碼優化

    · 靜態編譯優化

    · JIT運行優化


 

初步認識

Java里程碑(關鍵部分)

1. 2004年,JDK 1.5發佈。同時改名爲J2SE 5.0。Java語言大量改進,好比支持泛型、註解、自動裝箱、枚舉類型、可變長參數、加強的foreach循環等。

2. 2011年,JDK 1.7發佈。正式啓用新垃圾回收器G1,支持64位系統的壓縮指針,NIO 2.0,新增invokedynamic指令。

3. 2014年,JDK 1.8發佈。全新的Lambda表達式完全改變Java編程風格和習慣。

4. 2016年,JDK 1.9發佈。最使人期待的功能應該是Java的模塊化。

理解虛擬機

1. 虛擬機。

    a) 虛擬的計算機。

    b) 軟件,執行一系列虛擬計算機指令。

2. 分類。

    a) 系統虛擬機:對物理計算機的仿真,提供一個可運行完整操做系統的軟件平臺。

    b) 程序虛擬機:爲執行單個計算機程序而設計,如Java字節碼指令。

Java虛擬機種類

被大規模部署和應用的是Hotspot虛擬機。

Java語言規範

1. 語言規範:定義Java語言特性,如Java語法、詞法、數據類型、變量類型、數據類型轉換約定、數組、異常等。

2. 詞法:規定每一個單詞如何書寫,如關鍵字、標識符等。

3. 語法:規定語句如何書寫,如if語句等。

4. 官方文檔:http://docs.oracle.com/javase/specs/。

Java虛擬機規範

1. 虛擬機規範大概組成:

    a) 定義虛擬機的內部結構;

    b) 定義虛擬機執行的字節碼類型和功能;

    c) 定義Class文件的結構;

    d) 定義類的裝載、鏈接和初始化。

2. Java虛擬機執行Java字節碼,而運行的Java字節碼未必由Java語言編寫,如Groovy、Scala等均可以生成Java字節碼。

3. 官方文檔:http://docs.oracle.com/javase/specs/。

基本結構

Java堆(Heap)

1. 存儲:幾乎全部的對象。

2. 使用者:全部線程共享。

3. 結構:根據垃圾回收機制,通常劃分爲

    a) 新生代(New/Young Generation):新生對象或年齡不大的對象。又可能分爲eden區、s0區(也稱from區)、s1區(也稱to區,from/to合稱Survivor區)。from和to是兩塊大小相等、能夠互換角色的內存空間。

    b) 老年代(Tenured Generation):老年對象。

4. 過程:絕大多數狀況下,對象首先分配在eden,一次新生代回收後,若是對象存活,則進入s0或s1。以後,每經一次新生代回收,若是對象存活,則年齡加1。當對象年齡達到必定條件後,則進入老年代。

5. 舉例。

複製代碼
 1 public class SimpleHeap {  2  3 private int id;  4  5 public SimpleHeap(int id) {  6 this.id = id;  7  }  8  9 public void show() { 10 System.out.println("My ID is " + id); 11  } 12 13 public static void main(String[] args) { 14 SimpleHeap s1 = new SimpleHeap(1); 15 SimpleHeap s2 = new SimpleHeap(2); 16  s1.show(); 17  s2.show(); 18  } 19 20 }
複製代碼

Java棧(Stacks)

1. 存儲:線程執行的基本行爲是函數調用,每次函數調用的數據都經過Java棧傳遞。

2. 使用者:線程私有。

3. 結構:棧數據結構(先進後出),棧元素爲棧幀。一個棧幀至少包括局部變量表、操做數棧和幀數據區。

4. 過程:每一次函數調用,都有一個對應的棧幀入棧,每一次函數調用結束(return指令或拋出異常),都有一個棧幀出棧。

5. 局部變量表。

    a) 做用:保存函數的參數和局部變量。

    b) 其中的變量只在當前函數調用有效,函數調用結束後銷燬。

6. 操做數棧。

    a) 做用:保存計算過程當中的中間結果,同時做爲計算過程當中變量臨時的存儲空間。

    b) 舉例:iadd指令會在操做數棧中彈出兩個整數並相加,計算結果再入棧。

7. 幀數據區。

    a) 做用:方便訪問常量池;函數返回或異常後,恢復調用者函數的棧幀。

    b) 存儲:常量池指針;異常處理表。

8. 棧上分配。

    a) 一項虛擬機優化技術。

    b) 思想:對於線程私有(不可能被其餘線程訪問)的對象,將它們打散分配在棧上,而不是在堆上。函數調用結束後自行銷燬,無需垃圾回收器介入。

    c) 基礎:逃逸分析,判斷對象做用域是否可能逃逸出函數體。

    d) 舉例。

複製代碼
 1 public class EscapeAnalysis {  2  3 private static User user;  4  5 public static void test1() {  6 // 逃逸對象  7 user = new User();  8 user.id = 1;  9 user.name = "test1"; 10  } 11 12 public static void test2() { 13 // 非逃逸對象 14 User user = new User(); 15 user.id = 2; 16 user.name = "test2"; 17  } 18 19 } 20 21 class User { 22 23 int id; 24 25  String name; 26 27 }
複製代碼

方法區(Method Area)

1. 存儲:類信息,如類字段、方法、常量池(字符串字面量、數字常量)等。

2. 使用者:線程共享。

3. 結構:JDK 1.六、JDK 1.7中,即永久區(Perm);JDK 1.8中,即元數據區(Metaspace,永久區已被移除)。注意:方法區是Java虛擬機規範的概念,永久區、元數據區是Hotspot對方法區的實現。

直接內存(Direct Memory)

1. 使用者:NIO庫,如ByteBuffer。

2. 特色:

    a) 直接向操做系統申請的內存空間,性能優於Java堆。

    b) 大小受限於操做系統分配的最大內存。

本地方法棧(Native Method Stacks)

與Java棧很是相似,不一樣在於用於本地方法調用。

經常使用參數

設置參數

java [-options] class [args...]

1. -options表示Java虛擬機啓動參數。

2. class爲帶有main()函數的Java類。

3. args表示傳遞給主函數main()的參數。

查看參數

1. 「-XX:+PrintVMOptions」:打印虛擬機接受到的參數(顯式參數)。

2. 「-XX:+PrintCommandLineFlags」:打印虛擬機顯式和隱式參數。

跟蹤垃圾回收

1. 「-XX:+PrintGC」。

    a) GC時打印簡單日誌。

    b) 舉例:2次新生代GC,1次Full GC。

[GC (Allocation Failure)  42000K->36496K(56320K), 0.0009016 secs]
[GC (Allocation Failure) -- 41616K->41616K(56320K), 0.0020593 secs] [Full GC (Ergonomics) 41616K->5655K(56320K), 0.0070610 secs]

2. 「-XX:+PrintGCDetails」。

    a) GC時打印詳細日誌。

    b) 舉例:2次新生代GC,1次Full GC。第1 次新生代從6570K降至648K,堆從42410K降至36496K,user表示用戶態CPU耗時,sys表示系統CPU耗時,real表示GC實際耗時。第3次Full GC對新生代、老年代和元數據區回收。

[GC (Allocation Failure) [PSYoungGen: 6570K->648K(15360K)] 42410K->36496K(56320K), 0.0008117 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) --[PSYoungGen: 5768K->5768K(15360K)] 41616K->41616K(56320K), 0.0010395 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Ergonomics) [PSYoungGen: 5768K->0K(15360K)] [ParOldGen: 35848K->5659K(40960K)] 41616K->5659K(56320K), [Metaspace: 2517K->2517K(1056768K)], 0.0065072 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

    c) 虛擬機在退出時,打印堆的詳細信息。3個16進制數字分別表示下界、當前上界和上界。以eden爲例,(上界0x00000000ff600000-下界0x00000000fec00000)/1024=10240K。

複製代碼
Heap
 PSYoungGen      total 15360K, used 5188K [0x00000000fec00000, 0x0000000100000000, 0x0000000100000000) eden space 10240K, 50% used [0x00000000fec00000,0x00000000ff111298,0x00000000ff600000) from space 5120K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffb00000) to space 5120K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x0000000100000000) ParOldGen total 40960K, used 5659K [0x00000000fc400000, 0x00000000fec00000, 0x00000000fec00000) object space 40960K, 13% used [0x00000000fc400000,0x00000000fc986ee0,0x00000000fec00000) Metaspace used 2523K, capacity 4486K, committed 4864K, reserved 1056768K class space used 273K, capacity 386K, committed 512K, reserved 1048576K
複製代碼

3. 「-XX:+PrintHeapAtGC」。

    a) 堆GC時打印先後日誌。

    b) 舉例:1次堆GC。

複製代碼
{Heap before GC invocations=40 (full 0):
 PSYoungGen      total 583680K, used 583110K [0x0000000780a00000, 0x00000007bfb00000, 0x00000007c0000000) eden space 582144K, 99% used [0x0000000780a00000,0x00000007a42635c8,0x00000007a4280000) from space 1536K, 70% used [0x00000007bf880000,0x00000007bf98e5f0,0x00000007bfa00000) to space 1024K, 0% used [0x00000007bfa00000,0x00000007bfa00000,0x00000007bfb00000) ParOldGen total 131072K, used 2060K [0x0000000701e00000, 0x0000000709e00000, 0x0000000780a00000) object space 131072K, 1% used [0x0000000701e00000,0x0000000702003050,0x0000000709e00000) Metaspace used 2539K, capacity 4486K, committed 4864K, reserved 1056768K class space used 275K, capacity 386K, committed 512K, reserved 1048576K Heap after GC invocations=40 (full 0): PSYoungGen total 556544K, used 578K [0x0000000780a00000, 0x00000007bfb00000, 0x00000007c0000000) eden space 555520K, 0% used [0x0000000780a00000,0x0000000780a00000,0x00000007a2880000) from space 1024K, 56% used [0x00000007bfa00000,0x00000007bfa90928,0x00000007bfb00000) to space 1536K, 0% used [0x00000007bf800000,0x00000007bf800000,0x00000007bf980000) ParOldGen total 131072K, used 2574K [0x0000000701e00000, 0x0000000709e00000, 0x0000000780a00000) object space 131072K, 1% used [0x0000000701e00000,0x0000000702083978,0x0000000709e00000) Metaspace used 2539K, capacity 4486K, committed 4864K, reserved 1056768K class space used 275K, capacity 386K, committed 512K, reserved 1048576K }
複製代碼

4. 「-XX:+PrintGCTimeStamps」:打印虛擬機啓動後GC發生的時間偏移量。

5. 「-XX:+PrintGCApplicationStoppedTime」。

    a) GC時打印停頓時間。

    b) 舉例:3次GC停頓時間。

Total time for which application threads were stopped: 0.0006926 seconds, Stopping threads took: 0.0000476 seconds Total time for which application threads were stopped: 0.0012608 seconds, Stopping threads took: 0.0000349 seconds Total time for which application threads were stopped: 0.0012115 seconds, Stopping threads took: 0.0000374 seconds

6. 「-XX:+PrintReferenceGC」:打印軟運用、弱引用、虛引用和Finallize隊列。

7. 「-Xloggc」:GC日誌輸出到文件。例如:

-Xloggc:E:/gc.log

跟蹤類加載/卸載

1. 「-verbose:class」:打印類加載和卸載。

2. 「-XX:+TraceClassLoading」:打印類加載。

3. 「-XX:+TraceClassUnloading」:打印類卸載。

4. 類存在形式:

    a) 通常,類以jar打包或class文件形式存放在文件系統。

    b) ASM等在運行時動態生成類。

設置初始堆和最大堆

1. 「-Xms」:堆初始大小。

2. 「-Xmx」:堆最大大小。

3. 技巧:實際工做中,設置初始堆和最大堆相等,能夠減小垃圾回收次數,提供性能。

設置堆分佈

1. 「-Xmn」:新生代大小,同時影響老年代大小。

2. 「-XX:NewRatio」:新生代和老年代比例,即老年代/新生代,與「-Xmn」做用相同(舊參數)。

3. 「-XX:SurvivorRatio」:edit區和from/to區的比例,即eden/from和eden/to。

4. 技巧。

    a) 新生代大小對系統性能及GC有很大影響。

    b) 實際工做中,應根據系統特色合理設置堆分佈,基本策略是:儘量將對象預留在新生代,減小老年代GC的次數。

    c) 通常,新生代設置成整個堆的1/4~1/3左右。

5. 舉例。

    a) 代碼。

複製代碼
 1 public class NewSizeDemo {  2  3 public static void main(String[] args) {  4 byte[] b = null;  5 for (int index = 0; index < 10; index++) {  6 b = new byte[5 * 1024 * 1024];  7  }  8  }  9 10 }
複製代碼

    b) 參數:兩組參數等效。

-Xms60m -Xmx60m -Xmn20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails
-Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=2 -XX:+PrintGCDetails

    c) 結構。

空間

大小

60MB

新生代

20MB

eden

10MB

from

5MB

to

5MB

老年代

40MB

    d) 結果:eden沒法容納數組,發生GC。注意:雖然經過地址計算新生代大小爲(0x0000000100000000-0x00000000fec00000)/1024=20480KB,但因爲垃圾回收須要和對齊等緣由,實現大小(15360K)會有損失。

複製代碼
[GC (Allocation Failure) [PSYoungGen: 6365K->616K(15360K)] 42205K->36456K(56320K), 0.0024206 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) --[PSYoungGen: 5736K->5736K(15360K)] 41576K->41584K(56320K), 0.0031165 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Ergonomics) [PSYoungGen: 5736K->0K(15360K)] [ParOldGen: 35848K->5655K(40960K)] 41584K->5655K(56320K), [Metaspace: 2483K->2483K(1056768K)], 0.0070298 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Heap PSYoungGen total 15360K, used 5222K [0x00000000fec00000, 0x0000000100000000, 0x0000000100000000) eden space 10240K, 51% used [0x00000000fec00000,0x00000000ff119b20,0x00000000ff600000) from space 5120K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffb00000) to space 5120K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x0000000100000000) ParOldGen total 40960K, used 5655K [0x00000000fc400000, 0x00000000fec00000, 0x00000000fec00000) object space 40960K, 13% used [0x00000000fc400000,0x00000000fc985f98,0x00000000fec00000) Metaspace used 2489K, capacity 4486K, committed 4864K, reserved 1056768K class space used 273K, capacity 386K, committed 512K, reserved 1048576K
複製代碼

處理堆溢出

1. 「-XX:+HeapDumpOnOutOfMemoryError」:堆溢出(OOM)時導出信息。

2. 「-XX:HeapDumpPath」:堆溢出時導出的路徑。使用MAT等工具可分析文件。

3. 「-XX:OnOutOfMemoryError」:堆溢出時執行腳本,可用於奔潰程序自救、報警、通知等。

4. 舉例:使用下面的參數運行NewSizeDemo。

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test.dump -XX:OnOutOfMemoryError=D:/shell.bat

配置方法區

1. 「-XX:PermSize」:永久區初始大小(JDK 1.6/1.7)。

2. 「-XX:MaxPermSize」:永久區最大大小(JDK 1.6/1.7)。

3. 「-XX:MaxMetaspaceSize」:元數據區最大大小(JDK 1.8)。

4. 技巧。

    a) 動態生成大量類(如動態代理、AOP)時,可能致使方法區溢出(OOM)。

    b) JDK 1.6/1.7,默認永久區最大大小爲64MB;JDK 1.8,默認元數據區耗盡全部可用系統內存。

配置棧

1. 「-Xss」:單個線程的棧最大大小。

2. 「-XX:+DoEscapeAnalysis」:開啓逃逸分析(僅限Server模式下使用)。

3. 「-XX:+EliminateAllocations」:開啓標量替換(默認已開啓,容許對象打散分配在棧上)。

4. 技巧:

    a) 「-Xss」影響函數調用深度、局部變量大小等。

    b) 棧上分配速度快,同時避免垃圾回收,但棧相比堆較小,不適合大對象。

5. 舉例:測試函數調用深度。

    a) 代碼。

複製代碼
 1 public class TestStackDeep {  2  3 private static int count = 0;  4  5 public static void recursion() {  6 count++;  7  recursion();  8  }  9 10 public static void main(String[] args) { 11 try { 12  recursion(); 13 } catch (StackOverflowError e) { 14 System.out.println("deep of calling = " + count); 15  e.printStackTrace(); 16  } 17  } 18 19 }
複製代碼

    b) 參數。

-Xss128k

    c) 結果。

deep of calling = 1087
java.lang.StackOverflowError
    at gz.jvm.TestStackDeep.recursion(TestStackDeep.java:8)

配置直接內存

1. 「-XX:MaxDirectMemorySize」:直接內存最大大小。如不設置,默認爲「-Xmx」。

2. 技巧:直接內存適合申請次數較少、訪問較頻繁的場景。由於申請堆空間的速度遠遠高於直接內存。

配置工做模式

1. 「-client」:Client模式。

2. 「-server」:Server模式。

3. 「-version」:查看模式。

4. 技巧。

    a) Client模式:啓動速度較快。適合用戶界面,運行時間不長。

    b) Server模式:啓動速度較慢(啓動時嘗試收集更多系統性能信息,使用更復雜的優化算法優化程序),徹底啓動並穩定後,執行速度遠遠快於Client模式。適合後臺長期運行的系統。

    c) 兩種模式下的各類參數默認值可能不一樣,可以使用「-XX:+PrintFlagsFinal」參數查看。

垃圾回收算法

垃圾回收

1. 垃圾:存在於內存中的、不會再被使用的對象。

2. 回收:將內存空間空閒的區域騰出來。

3. 若是大量不會被使用的對象一直佔用空間不放,須要內存空間時,沒法使用這些被垃圾對象佔用的內存,從而有可能致使內存溢出。

引用計數法(Reference Counting)

1. 原理:對於一個對象A,只要有任何一個對象引用了A,則A的引用計數器加1;當引用失效時,引用計數器減1。當對象A的引用計數器值爲0,則對象A不可能再被使用。

2. 特色:

    a) 簡單;

    b) 最經典、最古老的垃圾回收算法。

3. 問題:

    a) 沒法處理循環引用的狀況。

        i. 可達對象:經過根對象進行引用搜索,最終能夠達到的對象。

        ii. 不可達對象:經過根對象進行引用搜索,最終沒有被引用到的對象。

    b) 每次引用產生和失效時,引用計數器的加、減法操做對性能有必定影響。

4. 應用:Java虛擬機未採用。

標記清除法(Mark-Sweep)

1. 原理:將垃圾回收分爲標記階段和清除階段。

    a) 標記階段:從跟節點開始,標記全部可達對象。

    b) 清除階段:清除未被標記的垃圾對象。

2. 特色:現代垃圾回收算法的思想基礎。

3. 問題:回收後的空間不連續(碎片)。空間分配時,尤爲大對象內存分配時,不連續的內存空間工做效率低於連續空間。

複製算法(Coping)

1. 原理:將內存空間分爲兩塊,每次只使用其中一塊。垃圾回收時,將正在使用的一塊內存中的存活對象複製到未使用的一塊中,再清除正在使用的內存中的全部對象,最後交換兩塊內存的角色。

2. 特色:

    a) 若是垃圾對象多,則複製的存活對象相對較少,複製算法效率就高(如新生代)。

    b) 回收後的內存空間沒有碎片。

3. 缺點:內存摺半。

4. 應用:新生代串行垃圾回收器。eden中的存活對象被複制到未使用的survivor中(假設是to),正在使用的survivor(假設是from)中的年輕對象也被複制到to中(大對象或老年對象直接進入老年代,若是to已滿,則對象也直接進入老年代)。清除eden和from中的剩餘垃圾對象。

標記壓縮法(Mark-Compact)

1. 原理:標記清除算法的優化版。三步:

    a) 從根節點開始,標記全部可達對象;

    b) 將全部存活對象壓縮到內存的一端;

    c) 清理邊界外全部空間。

2. 特色:

    a) 回收後的內存空間沒有碎片。

    b) 無內存摺半。

3. 應用:老年代回收。

分代算法(Generational Collecting)

1. 原理:根據對象的特色將內存空間分爲幾塊,每塊內存採用不一樣的回收算法。

2. 應用:Java虛擬機。

    a) 新生代特色是對象朝生夕滅,約90%對象很快被回收,適合複製算法。

    b) 老年代的回收性價比低於新生代,適合標記壓縮或標記清除算法。

    c) 一般,新生代回收頻率高,回收耗時短;老年代回收頻率低,回收耗時較長。

3. 卡表(Card Table):一個比特位集合,每個比特位表示老年代的某一區域中的對象是否持有新生代的引用。新生代GC時,根據卡表掃描老年代對象,而避免掃描全部老年代對象。下圖,每一位表示老年代4KB的空間。

分區算法(Region)

1. 原理:將堆劃分紅連續的不一樣小區間,每一個小區間都獨立使用、獨立回收。

2. 特色:

    a) 可控制每次回收的小區間個數;

    b) 避免回收整個堆,減小GC停頓時間。

判斷可觸及性

1. 可觸及性的3中狀態。

    a) 可觸及的:從根節點開始,可達對象。

    b) 可復活的:對象的全部引用都被釋放,但在finalize()函數復活(注意finalize()只會執行一次)。

    c) 不可觸及的:對象的全部引用都被釋放,且執行finalize()函數後未復活。

2. 回收依據:不可觸及的對象。

3. 對象復活舉例。

複製代碼
 1 public class CanReliveObj {  2  3 public static CanReliveObj obj;  4  5  @Override  6 protected void finalize() throws Throwable {  7 super.finalize();  8 System.out.println("CanReliveObj.finalize()");  9 obj = this; 10  } 11 12 public static void main(String[] args) throws Exception { 13 obj = new CanReliveObj(); 14 15 obj = null; 16  System.gc(); 17 Thread.sleep(1000); 18 // 因爲在finalize()後復活,因此打印結果不爲null 19 System.out.println("obj = " + obj); 20 21 obj = null; 22  System.gc(); 23 Thread.sleep(1000); 24 // 因爲finalize()只執行一次,因此不可能再復活,打印爲null 25 System.out.println("obj = " + obj); 26  } 27 28 }
複製代碼

4. 不建議使用finalize()釋放資源,緣由:

    a) 無心中復活對象;

    b) finalize()被系統調用,調用時間不肯定,推薦使用「try-catch-finally」釋放資源。

5. 4種引用類型。

    a) 對比。

類型

被回收時間

是否引發OOM

應用

強引用

寧願OOM也不回收

 

軟引用

內存緊張時

無關緊要的緩存

弱引用

GC時

無關緊要的緩存

虛引用

隨時

跟蹤對象回收時間

    b) 代碼。

複製代碼
 1 import java.lang.ref.PhantomReference;  2 import java.lang.ref.ReferenceQueue;  3 import java.lang.ref.SoftReference;  4 import java.lang.ref.WeakReference;  5  6 public class ReferenceDemo {  7  8 public static void main(String[] args) {  9 // 強引用 10 ReferenceDemo strongReference = new ReferenceDemo(); 11 // 軟引用 12 SoftReference<ReferenceDemo> softReference = new SoftReference<ReferenceDemo>(new ReferenceDemo()); 13 // 弱引用 14 WeakReference<ReferenceDemo> weakReference = new WeakReference<ReferenceDemo>(new ReferenceDemo()); 15 // 虛引用 16 ReferenceQueue<ReferenceDemo> referenceQueue = new ReferenceQueue<ReferenceDemo>(); 17 PhantomReference<ReferenceDemo> phantomReference = new PhantomReference<ReferenceDemo>(new ReferenceDemo(), referenceQueue); 18  } 19 20 }
複製代碼

停頓現象

1. Stop-The-World(STW):垃圾回收時,會產生應用程序的停頓,整個應用被卡死,沒有任何響應。

2. 目的:終止全部線程執行,此時沒有新的垃圾產生,保證系統狀態在一個瞬間的一致性,益於標記垃圾對象。

垃圾回收器

串行回收器

1. 特色:

    a) 僅適用單線程垃圾回收;

    b) 獨佔式垃圾回收(回收時全部線程暫停,即STW);

    c) 成熟。

2. 適合:單CPU。

3. 參數:

    a) 「-XX:+UseSerialGC」:新生代、老年代都使用串行回收器。

    b) 「-XX:+UseParNewGC」:新生代使用ParNew回收器,老年代使用串行回收器。

    c) 「-XX:+UseParallelGC」:新生代使用ParallelGC回收器,老年代使用串行回收器。

新生代ParNew回收器

1. 特色:

    a) 串行回收器的簡單多線程化;

    b) 獨佔式(STW)。

2. 適合:併發能力較強的CPU(單CPU上效果不比串行回收器好)。

3. 參數:

    a) 「-XX:+UseParNewGC」:新生代使用ParNew回收器,老年代使用串行回收器。

    b) 「-XX:+UseConcMarkSweepGC」:新生代使用ParNew回收器,老年代使用CMS回收器。

    c) 「-XX:ParallelGCThread」:指定線程數,通常最好與CPU數量至關。默認時,若CPU數量≤8,則爲CPU數量;不然爲3+((5*CPU數量)/8)。

新生代ParallelGC回收器

1. 特色:

    a) 多線程;

    b) 獨佔式;

    c) 很是關注系統吞吐量。

2. 參數:

    a) 「-XX:+UseParallelGC」:新生代使用ParallelGC回收器,老年代使用串行回收器。

    b) 「-XX:+UseParallelOldGC」:新生代使用ParallelGC回收器,老年代使用ParallelOldGC回收器。

    c) 「-XX:MaxGCPauseMillis」:指定最大垃圾回收停頓時間。若是值太小,爲了達到預期停頓時間,虛擬機可能使用一個較小的堆(小堆比大堆回收快),致使垃圾回收頻繁,增長了垃圾回收總時間,下降了吞吐量。

    d) 「-XX:GCTimeRatio」:指定吞吐量大小,0~100的整數,默認99。若是爲n,則垃圾回收時間不超過1/(1+n)。

    e) 「-XX:+UseAdaptiveSizePolicy」:開啓自適應GC策略。新生代、eden和survivors的大小比例,晉升老年代對象的年齡等參數自動調整,以達到堆大小、吞吐量和停頓時間之間的平衡。適合手工調優困難的場景,僅指定最大堆「-Xmx」、目標吞吐量「-XX:GCTimeRatio」和停頓時間「-XX:MaxGCPauseMillis」便可。

    f) 注意:吞吐量「-XX:MaxGCPauseMillis」和停頓時間「-XX:GCTimeRatio」是相互矛盾的,不可兼得。減小停頓時間,同時會減小吞吐量;增長吞吐量,同時會增長停頓時間。

老年代ParallelOldGC回收器

1. 特色:

    a) 多線程;

    b) 獨佔式;

    c) 很是關注系統吞吐量;

    d) 僅JDK 1.6可使用。

2. 參數:「-XX:+UseParallelOldGC」:新生代使用ParallelGC回收器,老年代使用ParallelOldGC回收器。

CMS回收器

1. CMS:Concurrent Mark Sweep,併發標記清除。

2. 原理:

3. 特色:

    a) 多線程;

    b) 總體上,非獨佔式(應用程序運行時回收)。

3. 參數:

    a) 「-XX:+UseConcMarkSweepGC」:開啓CMS回收器。

    b) 「-XX:ConcGCThread」:指定併發線程數。默認併發線程數是(並行線程數+3)/4,例如新生代ParNew回收器的並行線程數是「-XX:ParallelGCThread」。

    c) 「-XX:ParallelCMSThread」:同上。

    d) 注意:併發是指回收器和應用線程交替執行,並行是指應用程序中止,同時由多個線程一塊兒GC。CMS是併發的,因此當CPU緊張時,受到CMS線程的影響,應用程序的性能在GC時可能下降。

    e) 「-XX:CMSInitiatingOccupancyFraction」:達到該堆使用率閥值時開始回收。默認68,即堆使用率68%時回收。因爲CMS是併發的,GC時應用程序未中斷,該閥值保證應用程序仍有足夠可用內存,而不是堆飽和時纔回收。調優技巧:若內存增加緩慢,則該值稍大,可有效下降CMS觸發頻率;反之,該值稍小,避免頻繁觸發老年代串行回收。

    f) 「-XX:UseCMSCompactAtFullCollection」:開啓CMS回收後內存碎片整理(即壓縮)。該整理不是併發的。

    g) 「-XX:CMSFullGCsBeforeCompaction」:指定多少次CMS後進行一次碎片整理。

    h) 「-XX:+CMSClassUnloadingEnabled」:開啓CMS回收Perm區。

G1回收器

1. G1回收器:Garbage-First(意爲優先收集垃圾比例高的區域),是JDK 1.7正式使用的全新垃圾回收器,長期目標是取代CMS回收器。

2. 特色:

    a) 並行性:多線程GC,有效利用多核。

    b) 併發性:部分工做可與應用程序同時執行,不會在整個回收期間阻塞應用程序。

    c) 分代GC:同時兼顧新生代和老年代(其餘回收器要麼工做在新生代,要麼老年代)。堆結構方面,並不要求整個eden區、新生代或老年代都連續。

    d) 空間整理:每次回收都會有效複製對象(適當移動),減小空間碎片。

    e) 可預見性:因爲分區緣由,只選取部分區域回收,較好地控制了全局停頓。

3. 過程:

    a) 新生代GC;

    b) 併發標記週期;

    c) 混合收集;

    d) 若是須要,可能FullGC。

4. 參數:

    a) 「-XX:+UseG1GC」:開啓G1。

    b) 「-XX:MaxGCPauseMillis」:指定目標最大停頓時間。G1會調整新生代和老年代比例、堆大小、晉升年齡等,試圖達到預設目標。但不可能兼得,停頓時間縮短,GC次數會增長。默認200。

    c) 「-XX:ParallelGCThread」:指定並行線程數。

    d) 「-XX:InitiatingHeapOccupancyPercent」:達到該堆使用率閥值時開始併發標記週期。默認45,即堆使用率45%時開始。若是該值偏大,會致使併發週期遲遲不啓動,引發Full GC機率增長;若是該值太小,會併發週期頻繁,大量GC線程搶佔CPU,致使應用程序性能降低。

5. 詳情:http://www.oracle.com/technetwork/tutorials/tutorials-1876574.html。

垃圾回收其餘細節

禁用System.gc()

1. System.gc():默認時,觸發Full GC,對新生代、老年代回收。

2. 禁用緣由:通常認爲GC是自動的。

3. 禁用方法:「-XX:+DisableExplicitGC」參數。

開啓System.gc()併發回收

1. 開啓緣由:默認時,System.gc()使用傳統方式Full GC,忽略「-XX:+UseG1GC」和「-XX:+UseConcMarkSweepGC」參數,無併發執行。

2. 開啓方法:「-XX:+ExplicitGCInvokesConcurrent」參數。

對象什麼時候進入老年代

1. 老年對象進入老年代:對象經歷的GC次數達到「-XX:MaxTenuringThreshold」參數時,進入老年代。該參數默認爲15。

2. 大對象進入老年代:對象體積很大,新生代eden區和survivors區都沒法容納,則進入老年代。對於串行和ParNew回收器,晉升老年代體積閥值參數「-XX:PretenureSizeThreshold」,單位字節,默認爲0,即有運行狀況決定。

TLAB

1. TLAB:Thread Local Allocation Buffer,線程本地分配緩存。

2. 原理:

    a) 開啓TLAB時,虛擬機爲每個Java線程在eden區分配一塊TLAB空間。

    b) 因爲堆是全局共享的,堆上分配的對象都要同步,多線程競爭激烈時效率會降低。線程專屬的TLAB避免了多線程衝突,提升對象分配效率。

3. 參數:

    a) 「-XX:+UseTLAB」:開啓TLAB。默認已開啓。

4. 對象分配簡要流程。

性能監控工具

top命令

1. top命令:Linux命令,監控CPU、內存、進程。

2. 第1行:

    a) 任務隊列信息。

    b) 依次:系統當前時間、系統運行時間、當前登陸用戶數、平均負載(即任務隊列的平均長度)。

    c) 平均負載的3個值依次:1分鐘、5分鐘、15分鐘到如今平均值。

3. 第2行:

    a) 進程統計信息。

    b) 依次:正在運行的進程數、睡眠進程數、中止的進程數、殭屍進程數。

4. 第3行:

    a) CPU統計信息。

    b) 字段含義。

us

用戶空間CPU佔用率

sy

內核空間CPU佔用率

ni

用戶進程控件改變過優先級的進程CPU佔用率

id

空閒CPU佔用率

wa

等待輸入輸出的CPU時間百分比

hi

硬件中斷請求

si

軟件中斷請求

5. 第4行:

    a) 內存信息。

    b) 依次:物理內存總量、已使用物理內存、空間物理內存、內存緩衝表使用量。

6. 第5行:

    a) 交換區信息。

    b) 依次:交換區總量、已使用交換區、空閒交換區、緩衝交換區。

7. 進程信息區:

字段

含義

PID

進程ID

USER

全部者用戶名

PR

優先級

NI

nice值,負值爲高優先級、正值爲低優先級

VIRT

虛擬內存佔用總量,單位KB,VIRT=SWAP+RES

RES

使用的、未被換出的物理內存,單位KB,RES=CODE+DATA

SHR

共享內存,單位KB

%CPU

上次更新到如今的CPU時間佔用率

%MEM

物理內存佔用率

TIME+

使用的CPU時間總計,單位1/100秒

COMMAND

命令

vmstat命令

1. vmstat命令:Linux命令,監控CPU、內存、IO。

2. vmstat命令可指定採樣週期、採樣次數。

3. 字段含義。

procs

r

等待運行進程數

b

非中斷睡眠狀態進程數

memory

swpd

虛擬內存,單位KB

free

空閒內存,單位KB

buff

緩存內存,單位KB

swap

si

磁盤交換到內存的交換頁,單位KB/秒

so

內存交換到內存的交換頁,單位KB/秒

io

bi

發送到塊設備,單位塊/秒

bo

從塊設備接收,單位塊/秒

system

in

每秒中斷數,包括時鐘中斷

cs

每秒上下文切換次數

cpu

us

用戶CPU使用時間

sy

系統CPU使用時間

id

空間CPU時間

iostat命令

1. iostat命令:Linux命令,監控IO。

2. 參數:

    a) 「-d」:僅輸出磁盤。

    b) 「-x」:輸出詳情。

3. 字段含義。

tps

每秒傳輸次數

kB_read/s

每秒從設備讀取數據量

kB_wrtn/s

每秒向設備寫入數據量

kB_read

讀取總數據量

kB_wrtn

寫入總數據量

pidstat命令

1. pidstat命令:Linux命令,監控進程、線程。

2. 參數:

    a) 「-p」:進程ID。

    b) 「-u」:監控CPU。

    c) 「-r」:監控內存。

    d) 「-d」:監控IO。

    e) 「-t」:顯示線程。

3. 舉例:

    a) 監控進程CPU。

    b) 監控線程CPU。

    c) 監控內存。

字段

含義

minflt/s

進程每秒minor faults(無需從磁盤調出內存頁)的總數

majflt/s

進程每秒major faults(需從磁盤調出內存頁)的總數

VSZ

虛擬內存,單位KB

RSS

物理內存,單位KB

%MEM

內存佔用率

    d) 監控線程IO。

jps命令

1. jps命令:JDK工具,列出Java進程。

2. 參數:

    a) 「-m」:顯示main()函數的參數。

    b) 「-l」:顯示main()函數所在類的完整包名+類名。

    c) 「-v」:顯示傳遞給JVM的參數。

jstat命令

1. jstat命令:JDK工具,查看虛擬機運行信息。

2. 我的見解:該命令參數較多,不如用其餘可視化工具代替。

jinfo命令

1. jinfo命令:JDK工具,查看正在運行虛擬機的參數及修改部分參數。

2. 語法:

jinfo <option> <pid>

    option:

    a) -flag <name>:打印指定參數。

    b) -flag [+|-]<name>:設置參數的布爾值。

    c) -flag <name>=<value>:設置參數。

jmap命令

1. jmap命令:JDK工具,生成堆dump文件(即堆快照),及查看堆內對象統計信息。

2. 可以使用jhat命令、Visual VM、MAT等工具分析dump文件。

3. 參數:

    a) 「-head」:顯示堆信息。

    b) 「-histo[:live]」:顯示對象統計信息,可指定僅統計存活(「:live」子參數)

    c) 「-clstats」:ClassLoader統計信息。

    d) 「-dump:<dump-options>」:導出堆,子參數有

        i. 「live」:僅存活對象;

        ii. 「format=b」:二進制格式;

        iii. 「file=<file>」:導出文件路徑。

jhat命令

1. jhat命令:JDK工具,分析堆dump文件。

2. 我的見解:使用Visual VM更方便。

3. 用法舉例:

    a) 執行命令;

    b) 訪問http://localhost:7000。

jstack命令

1. jstack命令:JDK工具,查看線程棧(也稱線程dump)。

2. 參數:「-l」附加信息。

jcmd命令

1. jstack命令:JDK 1.7之後的工具,多功能工具,可導出堆、查看進程、查看線程棧、執行GC等。

2. 待補充。

Visual VM工具

1. Visual VM:JDK 1.6 Update 7之後,功能強大的多合一故障診斷和性能監控可視化工具。

2. 代替:jstat、jmap、jhat、jstack等命令,甚至JConsole。

3. 主要功能。

    a) 鏈接應用程序:本地或JMX遠程。

    b) 監控概況:JVM參數、CPU、內存、堆、線程等。

    c) 線程dump及分析。

    d) 堆dump及分析,支持OQL。

    e) BTrace:不停機狀態,注入字節碼動態監控運行狀況。

Mission Control工具

1. Mission Control:JDK 1.7 Update 40之後,診斷工具。

2. 我的見解:雖然貌似比Visual VM好,但Visual VM夠用且市場佔有率更高。

解決OOM問題

堆溢出

1. 現象。

2. 2種解決方法。

    a) 增大「-Xmx」參數。

    b) Visual VM工具分析大量佔用堆的對象。

直接內存溢出

1. 現象。

2. 2種解決方法。

    a) 顯式GC「System.gc()」。

    b) 不設置「-XX:MaxDirectMemorySize」參數(此時等於「-Xmx」)或增大該值。

過多線程致使溢出

1. 現象。

2. 2種解決方法。

    a) 減少「-Xmx」參數,操做系統可預留更多內存用於建立線程。

    b) 減少「-Xss」參數。

永久區/元數據區溢出

1. 現象。

2. 3中解決方法。

    a) 增大「-XX:MaxPermSize」/「-XX:MaxMetaspaceSize」參數。

    b) 減小須要的類數量。

    c) 使用ClassLoader合理裝載類,並按期回收。

GC效率低致使OOM

1. 現象。

2. 緣由:當下列條件都知足時,虛擬機認爲GC效率低下,拋出OOM。

    a) 花在GC上的時間超過98%;

    b) 老年代釋放內存小於2%;

    c) eden區釋放內存小於2%;

    d) 最近連續5次GC都出現上述狀況(同時出現,不是出現一個)。

3. 解決方法:該OOM屬輔助做用,提示堆過小,添加「-XX:-UseGCOverheadLimit」參數禁用該功能。

String實現細節

String對象特色

1. 不變性(immutable)。

    a) 解釋:String對象一旦生成,不能再修改。

    b) 好處:多線程共享,並頻繁訪問時,可省略同步和鎖的時間,提升性能。

2. 針對常量池的優化。

    a) 解釋:String對象值相同時,它們只引用常量池的同一個拷貝。

    b) 好處:節省內存。

3. 類的final定義。

    a) 解釋:String類不可能有任何子類。

    b) 好處:保安全性保護。

String常量池

1. 常量池:專門存放String常量的區域。

2. 位置:

    a) JDK 1.6以前,位於永久區;

    b) JDK 1.7之後,位於堆。

字節碼優化

執行字節碼從兩處優化:javac編譯時;經過JIT在運行時。

靜態編譯優化

1. 編譯時計算。

    a) 解釋:若是計算表達式的值能在編譯時肯定,則表達式計算提早到編譯階段。

    b) 舉例1:

    c) 舉例2:

2. 變量字符串的鏈接。

    a) 解釋:變量字符串鏈接被轉爲StringBuilder操做,避免每次字符串操做產生新對象。

    b) 舉例:自動優化先後的代碼。

3. 基於常量的條件語句裁剪。

    a) 解釋:任何邏輯在編譯時就肯定時,不須要的邏輯會被裁剪。

    b) 舉例:實際多餘的if...else...在編譯器被裁剪。

4. switch語句優化。

    a) 解釋:switch語句可生成tableswitch或loopupswith字節碼指令,前者效率高於後者,前者只能處理case連續的值,後者可處理不連續的值。

    b) 舉例:雖然不連續,但因爲離散空間不大,編譯器插入值填充使用tableswitch。

JIT運行優化

1. JIT:Just-In-Time,即時編譯。

2. 目的:避免函數被解釋執行,而是將整個函數體編譯成機器碼,每次只執行編譯編譯後的機器碼,可大幅度提高效率。

3. 虛擬機3種執行模式。

    a) 解釋執行(Interpreted Mode):不作JIT編譯。

    b) 混合模式(Mixed Mode):默認。熱點代碼被編譯執行,其餘解釋執行。熱點代碼的判斷依據是調用頻率。

    c) 編譯執行(Compiled Mode):全部函數編譯執行。

    d) 參數。

        i. 「-Xint」:解釋。

        ii. 「-Xmixed」:混合。

        iii. 「-XComp」:編譯。

        iv. 「-XX:CompileThreshold」:指定熱點代碼調用次數閥值。Client模式默認1500,Server模式默認10000。

        v. 「-XX:+PrintCompilation」:打印即時編譯日誌。

4. 多級編譯器。

    a) 客戶端編譯器(C1編譯器):Client模式使用的編譯器,編譯速度快。

    b) 服務端編譯器(C2編譯器):Server模式使用的編譯器,編譯優化多,編譯後代碼質量高,時間長於C1。

    c) 多級目的:在編譯速度和執行效率間取得平衡。

    d) 多級編譯5級:

        i. 0級(解釋執行):解釋執行,不採集性能監控數據;

        ii. 1級(簡單C1編譯):採用C1,最簡單的快速編譯,根據須要採集性能數據;

        iii. 2級(有限的C1編譯):採用C1,更多的優化編譯,可能根據1級採集的性能統計數據,進一步優化編譯代碼;

        iv. 3級(徹底C1編譯):徹底使用C1的全部功能,採集性能數據進行優化;

        v. 4級(C1編譯):徹底使用C1的全部功能,徹底優化。

    e) 參數:「-XX:+TieredCompilation」開啓多級編譯。

5. OSR棧上替換。

    a) 背景:函數要麼解釋,要麼編譯執行。從解釋切換到機器碼執行,若是還沒有準備好編譯版本,則解釋執行,下次才機器碼執行。絕大部分場合適用。

    b) 問題:不適合調用次數很少,但函數體內包含大量循環的函數。例如:

    c) OSR:On Stack Replacement棧上替換。不等待函數體運行結束,在循環體就將代碼替換爲編譯版本的技術。

6. 方法內聯。

    a) 目的:減小方法調用的次數,提升性能。JIT編譯器默認開啓。

    b) 參數:

        i. 「-XX:+Inline」:開啓方法內聯優化。

        ii. 「-XX:FreqInlineSize」:內聯體積上限,方法體積大於該值則不內聯。

    c) 舉例:優化先後。

7. 代碼緩存。

    a) 解釋:Code Cache。保存字節碼被編譯後的機器碼,緩存用完後JIT編譯中止,後續未編譯的字節碼解釋執行。代碼緩存的清理也由GC完成。

    b) 參數:「-XX:ReservedCodeCacheSize」指定緩存大小,默認32MB。

相關文章
相關標籤/搜索