10. JVM

1、初識 JVM

截圖

一、JDK,JRE 和 JVM

截圖

二、源碼到類文件

class Person{
	private String name="Jack";
    private int age;
	private final double salary=100;
    private static String address;
    private final static String hobby="Programming";
	//private Object obj=new Object();
    public void say(){
        System.out.println("person say...");
    }
    public static int calc(int op1,int op2){
        op1=3;
        int result=op1+op2;
        //Object o=obj;
        return result;
    }
	public static void main(String[] args){
		System.out.println(calc(1,2));
		say();
	}
}

javac Person.java ➡ Person.classhtml

Person.java ➡ 詞法分析器 ➡ tokens 流 ➡ 語法分析器 ➡ 語法樹 / 抽象語法樹 ➡ 語義分析器 ➡ 註解抽象語法樹 ➡ 字節碼生成器 ➡ Person.class 文件java

cafe babe 0000 0034 0038 0a00 0d00 2308
0024 0900 0c00 2506 4059 0000 0000 0000
0900 0c00 2609 0027 0028 0800 290a 002a
002b 0a00 0c00 2c0a 002a 002d 0700 2e07
002f 0100 046e 616d 6501 0012 4c6a 6176
612f 6c61 6e67 2f53 7472 696e 673b 0100
0361 6765 0100 0149 0100 0673 616c 6172
7901 0001 4401 000d 436f 6e73 7461 6e74
5661 6c75 6501 0007 6164 6472 6573 7301
0005 686f 6262 7908 0030 0100 063c 696e
6974 3e01 0003 2829 5601 0004 436f 6465
0100 0f4c 696e 654e 756d 6265 7254 6162
6c65 0100 0373 6179 0100 0463 616c 6301
0005 2849 4929 4901 0004 6d61 696e 0100
1628 5b4c 6a61 7661 2f6c 616e 672f 5374
7269 6e67 3b29 5601 000a 536f 7572 6365
4669 6c65 0100 0b50 6572 736f 6e2e 6a61
7661 0c00 1800 1901 0004 4a61 636b 0c00
0e00 0f0c 0012 0013 0700 310c 0032 0033
0100 0d70 6572 736f 6e20 7361 792e 2e2e
0700 340c 0035 0036 0c00 1d00 1e0c 0035
0037 0100 0650 6572 736f 6e01 0010 6a61
7661 2f6c 616e 672f 4f62 6a65 6374 0100
0b50 726f 6772 616d 6d69 6e67 0100 106a
6176 612f 6c61 6e67 2f53 7973 7465 6d01
0003 6f75 7401 0015 4c6a 6176 612f 696f
2f50 7269 6e74 5374 7265 616d 3b01 0013
6a61 7661 2f69 6f2f 5072 696e 7453 7472
6561 6d01 0007 7072 696e 746c 6e01 0015
284c 6a61 7661 2f6c 616e 672f 5374 7269
6e67 3b29 5601 0004 2849 2956 0020 000c
000d 0000 0005 0002 000e 000f 0000 0002
0010 0011 0000 0012 0012 0013 0001 0014
0000 0002 0004 000a 0015 000f 0000 001a
0016 000f 0001 0014 0000 0002 0017 0004
0000 0018 0019 0001 001a 0000 0032 0003
0001 0000 0012 2ab7 0001 2a12 02b5 0003
2a14 0004 b500 06b1 0000 0001 001b 0000
000e 0003 0000 0001 0004 0002 000a 0004
0001 001c 0019 0001 001a 0000 0025 0002
0001 0000 0009 b200 0712 08b6 0009 b100
0000 0100 1b00 0000 0a00 0200 0000 0900
0800 0a00 0900 1d00 1e00 0100 1a00 0000
2800 0200 0300 0000 0806 3b1a 1b60 3d1c
ac00 0000 0100 1b00 0000 0e00 0300 0000
0c00 0200 0d00 0600 0f00 0900 1f00 2000
0100 1a00 0000 2800 0300 0100 0000 0cb2
0007 0405 b800 0ab6 000b b100 0000 0100
1b00 0000 0a00 0200 0000 1200 0b00 1300
0100 2100 0000 0200 22

三、類加載機制

截圖

裝載git

​ (1) 經過一個類的全限定名獲取定義此類的二進制字節流github

​ (2) 將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構算法

​ (3) 在Java堆中生成一個表明這個類的java.lang.Class對象,做爲對方法區中這些數據的訪問入口數組

連接性能優化

​ (1) 驗證:保證被加載類的正確性數據結構

​ 【文件格式驗證/元數據驗證/字節碼驗證/符號引用驗證】多線程

​ (2) 準備jvm

​ 【爲類的靜態變量分配內存,並將其初始化爲默認值】

​ (3) 解析

​ 【把類中的符號引用轉換爲直接引用】

初始化

​ 對類的靜態變量,靜態代碼塊執行初始化操做

四、類加載器

截圖

雙親委派模型

​ 定義:若是一個類加載器在接到加載類的請求時,它首先不會本身嘗試去加載這個類,而是把這個請求任務委託給父類加載器去完成,依次遞歸,若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。

​ 優點:Java 類隨着加載它的類加載器一塊兒具有了一種帶有優先級的層次關係。好比,Object類,它存放在rt.jar之中,不管哪個類加載器要加載這個類,最終都是委派給處於模型最頂端的啓動類加載器進行加載,所以Object在各類類加載環境中都是同一個類。若是不採用雙親委派模型,那麼由各個類加載器本身取加載的話,那麼系統中會存在多種不一樣的類。

2、運行時數據區

截圖

截圖

一、方法區

(1)方法區是各個線程共享的內存區域,在虛擬機啓動時建立

(2)用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據

(3)雖然Java虛擬機規範把 方法區 描述爲堆的一個邏輯部分,可是它卻又一個別名叫作Non-Heap(非堆),目的是與Java堆區分開來

(4)當方法區沒法知足內存分配需求時,將拋出 OutOfMemoryError 異常

(5)方法區在JDK 8中就是Metaspace【元空間】,在 JDK6 或 7 中就是 Perm Space【永久代】

(6)Run-Time Constant Pool 在方法區分配

二、堆

(1)堆是Java虛擬機所管理內存中最大的一塊,在虛擬機啓動時建立,被全部線程共享

(2)Java對象實例以及數組都在堆上分配

(3)當堆沒法知足內存分配需求時,將拋出OutOfMemoryError異常

三、虛擬機棧

(1)虛擬機棧是一個線程執行的區域,保存着一個線程中方法的調用狀態。換句話說,一個Java線程的運行狀態,由一個虛擬機棧來保存,因此虛擬機棧確定是線程私有的,獨有的,隨着線程的建立而建立

(2)每個被線程執行的方法,爲該棧中的棧幀,即每一個方法的執行對應一個棧幀

(3)調用一個方法,就會向棧中壓入一個棧幀;一個方法調用完成,就會把該棧幀從棧中彈出

截圖

棧幀

​ 每一個棧幀對應一個被調用的方法,能夠理解爲一個方法的運行空間。

​ 每一個棧幀中包括局部變量表(Local Variables)、操做數棧(Operand Stack)、動態連接、方法返回地址(Return Address)和附加信息。

截圖

截圖

四、程序計數器

一個JVM進程中有多個線程在執行,而線程中的內容是否可以擁有執行權,是根據CPU調度來的。

假如線程A正在執行到某個地方,忽然失去了CPU的執行權,切換到線程B了,而後當線程A再獲

得CPU執行權的時候,怎麼能繼續執行呢?這就是須要在線程中維護一個變量,記錄線程執行到的位置。

(1)程序計數器佔用的內存空間很小,因爲Java虛擬機的多線程是經過線程輪流切換,並分配處理器執行時間的方式來實現的,在任意時刻,一個處理器只會執行一條線程中的指令。所以,爲了線程切換後可以恢復到正確的執行位置,每條線程須要有一個獨立的程序計數器(線程私有)

(2) 若是線程正在執行Java方法,則計數器記錄的是正在執行的虛擬機字節碼指令的地址

(3) 若是正在執行的是Native方法,則這個計數器爲空。

五、本地方法棧

若是當前線程執行的方法是 Native 類型的,這些方法就會在本地方法棧中執行。

截圖

3、JVM 內存模型

一、由對象推導內存模型

截圖

截圖

二、Young區

Young區分爲兩大塊,一個是Survivor區(S0+S1),一塊是Eden區。 Eden:S0:S1=8:1:1

三、Eden區

正常對象建立所在區域,大多數對象 "朝生夕死"

四、Survivor區

Survivor區分爲兩塊S0和S1,也能夠叫作From和To。

在同一個時間點上,S0和S1只能有一個區有數據,另一個是空的。

五、Old區

通常Old區都是年齡比較大的對象,或者相對超過了某個閾值的對象。

六、結合工具體驗與驗證

插件下載連接:https://visualvm.github.io/pluginscenters.html

(1)jvisualvm
(2)堆內存溢出:設置參數好比-Xmx20M -Xms20M
(3)方法區內存溢出:-XX:MetaspaceSize=50M -XX:MaxMetaspaceSize=50M
(4)虛擬機棧溢出:-Xss128k

4、垃圾回收

一、如何肯定一個對象是垃圾

(1)引用計數法

​ 一旦相互持有引用,就致使對象永遠無法回收【缺點,循環引用】

(2)可達性分析

​ 由 GC Root 出發,開始尋找,看看某個對象是否可達

​ GC Root:類加載器,Thread,本地變量表,static成員,經常使用引用,本地方法棧中的變量等

二、分代收集算法

截圖

截圖

截圖

分代收集算法

(1)標記清除【內存碎片】

(2)複製【複製多的比較耗性能】

(3)標記整理

Young區:複製算法

Old區:標記清楚或標記整理

三、垃圾收集器

(1)Serial
(2)Serial Old
(3)ParNew
(4)Parallel
(5)Parallel Old
(6)CMS
(7)G1

截圖

截圖

截圖

Parallel 相比ParNew,更加關注吞吐量

截圖

截圖

四、GC 知識梳理

截圖

截圖

截圖

5、工欲善其事必先利其器

一、JVM 參數

(1)標準參數

(2)-X參數

(3)-XX參數

​ -XX: [+/-] -XX: +UseG1GC

​ -XX: = -XX:InitialHeapSize=100M

(4)其餘參數

​ -Xms100M ➡ -XX:InitialHeapSize=100M

​ -Xms100M -Xss100

截圖

二、JVM 命令

(1)jps: 當前的java進程

(2)jinfo: 查看某個java進程目前的參數設置的狀況

(3)jstat: 查看java進程統計性能

(4)jstack:查看當前java進程的堆棧信息

(5)jmap:打印出堆轉存儲快照 jmap -heap PID

​ [dump出堆內存相關信息:jmap -dump:format=b,file=heap.hprof PID]

三、經常使用工具

(1) jconsole

(2) jvisualvm

(3) arthas

(4) mat/perfma:內存相關的信息

(5) gceasy.io/gcviewer

四、性能優化

截圖

OOM:經過MAT分析dump文件,定位OOM

GC優化

​ 經過不斷調整,觀察GC日誌的吞吐量和停頓時間,尋找最佳值

​ -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseG1GC

​ -Xloggc:gc.log

​ gceasy.io

​ 主要就是調整各類參數,垃圾收集器 ➡ 查看吞吐量和停頓時間的變量 [高吞吐量,低停頓時間]

截圖