JVM 虛擬機器

HuDu發表於2020-07-16

問題

首先來看看這些問題,下面會一一解答

  • jvm理解,Java8虛擬機器和之前有什麼更新
  • 什麼是OOM,什麼是StackOverFlowError
  • JVM常用的引數調優有哪些
  • 記憶體快照如何抓取,怎麼分析Fump檔案
  • 談談jvm中,類載入器的認識

基本概念

首先大致瞭解一下虛擬機器內部的東西

  • 程式計數器
  • Java虛擬機器棧
  • 本地方法棧
  • Java堆
  • 方法區

    程式計數器(PC)

程式計數器是一塊很小的記憶體空間,用於記錄下一條要執行的指令。每個執行緒都需要一個程式計數器,各個執行緒之中的計數器相互獨立,是執行緒中私有的記憶體空間

java虛擬機器棧

java虛擬機器棧也是執行緒私有的記憶體空間,它和java執行緒同一時間建立,儲存了區域性變數、部分結果,並參與方法的呼叫和返回

本地方法棧

本地方法棧和java虛擬機器棧的功能相似,java虛擬機器棧用於管理Java函式的呼叫,而本地方法棧用於管理本地方法的呼叫,但不是由Java實現的,而是由C實現的

java堆

為所有建立的物件和陣列分配記憶體空間,被JVM中所有的執行緒共享

方法區

也被稱為永久區,與堆空間相似,被JVM中所有的執行緒共享。方法區主要儲存的資訊是類的後設資料,方法區中最為重要的是類的型別資訊、常量池、域資訊、方法資訊,其中執行時常量池就在方法區,對永久區的GC回收,一是GC對永久區常量池的回收;二是永久區對後設資料的回收

類載入器

  1. 虛擬機器自帶的載入器
  2. 啟動類(根)載入器
  3. 擴充類載入器
  4. 應用程式載入器

雙親委託機制

  • Java 虛擬機器大致結構圖

JVM 虛擬機器

  • 詳細圖

JVM 虛擬機器

Class類是模板,而物件是類的例項,同一個類别範本也可創造不同的例項。
JVM 虛擬機器

Native

// native :凡是帶了native關鍵字的,說明java的作用範圍達不到了,回去呼叫底層C語言的庫!
//會進入本地方法棧
//呼叫本地方法本地介面JNI
// JNI作用:打展Java的使用,融合不同的程式語言為ava所用!最初: C、C++。
// Java 誕生的時候C、C++橫行,想要立足,必須要有調C、C++的程式
//它在記憶體區域中專門開昨了一塊標記區域: Native Method stack,登記native方法
//在最終執行的時候,載入本地方法庫中的方法透過INI
// Java程式驅動印表機

方法區

Method Area 方法區

方法區是被所有執行緒共享,所有欄位和方法位元組碼,以及一些特殊方法,如建構函式,介面程式碼也在此定義,簡單說,所有定義的方法的資訊都儲存在該區域,此區域屬於共享區間:
靜態變數、常量、類資訊(建構函式、介面定義)、執行時常量池存在方法區中,但是例項變數存在堆記憶體中,和方法區無關。
static  final  class  常量池
方法執行時入棧,方法的引用在棧,方法在堆裡面。
public class Test {
    private int a;
    private String name = "haha";

    public static void main(String[] args) {
        Test test1 = new Test();
        test1.a = 1;
        test1.name = "hudu";
    }
}

對於上面的程式碼,執行過程在底層的實現過程為:

JVM 虛擬機器

棧:資料結構

程式:資料結構+演算法
棧:先進後出、後進先出
佇列:先進先出(FIFO:First Input First Output)

為什麼main()方法先執行,最後結束
檢視下面的程式碼

public class Test2 {
    public static void main(String[] args) {
        new Test2().test();
    }

    public void test() {
        a();
    }
    public void a() {
        test();
    }
}

常見的棧溢位
最後會報錯:Exception in thread “main” java.lang.StackOverflowError

JVM 虛擬機器
由圖可知,正常的遞迴呼叫會有結束的限制,然而上面會方法之間不停呼叫,就出現了棧溢位。

棧:棧記憶體,主管程式的執行,生命週期和執行緒同步;執行緒結束,棧記憶體就釋放了,對於棧來說,不存在垃圾回收機制。
一旦執行緒結束,棧就 Over 了

棧:8大基本型別,物件的引用,例項的方法

棧執行原理:棧幀

JVM 虛擬機器
程式正在執行的方法一定在棧的頂部

棧+堆+方法區:互動關係
java8中,執行時常量池在方法區中,字串常量池,靜態變數在堆中,原空間方法實現。

JVM 虛擬機器

三種 JVM

  • Sun 公司 HotSpot
  • BEA
  • IBM

Heap,一個JVM 只有一個堆記憶體,堆記憶體是可以調整大小的

類載入器讀取了類檔案後,一般會把什麼放到堆中?類,方法,常量,變數。儲存我們所有引用型別的真實物件
類方法(靜態方法)儲存在方法去裡

堆記憶體彙總還要分為三個區域:

  • 新生區
  • 老年區
  • 永久區

JVM 虛擬機器

GC 垃圾回收主要在新生區和老年區
假設記憶體滿了,OOM,堆記憶體不夠!java. lang .OutOfMemoryError: Java hejap space

在JDK8之後,永久儲存區改了個名字(元空間)

新生區

  • 類:誕生和成長的地方,甚至死亡,
  • 伊甸園,所有的物件都在伊甸園區new出來的
  • 倖存者區(0,1)

JVM 虛擬機器
老年區
經過研究,99%的物件都是臨時物件

永久區
這個區域常駐記憶體,用來存放一些,jdk自身攜帶的Class物件,Interface後設資料,儲存的是java執行時的一些環境或類資訊,這個區域不存在垃圾回收機制,關閉jvm虛擬機器就會釋放這個區域的記憶體。

一個啟動類,載入了太多的第三方jar包,Tomcat部署了太多的應用。大量動態生成的反射類,不斷被載入,知道記憶體慢,出現OOM

  • jdk1.6之前:永久代,常量池在方法區中
  • jdk1.7:永久代,但是慢慢的退化,去永久代,常量池在堆中
  • jdk1.8之後:無永久代,常量池在元空間

JVM 虛擬機器

元空間,邏輯上存在,物理上不存在
下面透過程式碼驗證

public class Test4 {
    public static void main(String[] args) {
        //返回jvm的初始化總記憶體
        long total = Runtime.getRuntime().totalMemory();
        //返回jvm虛擬機器的使用的最大記憶體
        long max = Runtime.getRuntime().maxMemory();

        System.out.println("max="+max+"位元組\t"+(max/(double)1024/1024)+"MB");
        System.out.println("total="+total+"位元組\t"+(total/(double)1024/1024)+"MB");
        //預設情況下:分配總記憶體是電腦的1/4,而初始化是1/64

        //305664K+699392K = 1,005,056k = 981.5MB
    }
}

輸出結果:

max=1029177344位元組    981.5MB
total=1029177344位元組    981.5MB
Heap
 PSYoungGen      total 305664K, used 15729K [0x00000007aab00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 262144K, 6% used [0x00000007aab00000,0x00000007aba5c420,0x00000007bab00000)
  from space 43520K, 0% used [0x00000007bd580000,0x00000007bd580000,0x00000007c0000000)
  to   space 43520K, 0% used [0x00000007bab00000,0x00000007bab00000,0x00000007bd580000)
 ParOldGen       total 699392K, used 0K [0x0000000780000000, 0x00000007aab00000, 0x00000007aab00000)
  object space 699392K, 0% used [0x0000000780000000,0x0000000780000000,0x00000007aab00000)
 Metaspace       used 3093K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 335K, capacity 388K, committed 512K, reserved 1048576K

可以看到PSYoungGen+ParOldGen就等於總記憶體大小了
實際上方法區物理上不在堆記憶體中。

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

出現OOM:

  1. 嘗試擴大堆記憶體檢視結果
  2. 分析記憶體,看一下哪個地方出現問題

在一個專案中,突然出現OOM,如何排除

  • 能夠看到程式碼第幾行出錯:記憶體快照工具,MAT,Jprofile
  • Debug,一行一行分析程式碼!

MAT,Jprofile

  • 分析Dump記憶體檔案,快速定位記憶體洩漏
  • 獲得堆中資料
  • 獲得大的物件
  • 。。。
//-Xms設定初始化記憶體分配大小 1/64
//-Xmx設定最大分配記憶體大小,預設1/4
//-XX:+PrintGCDetails //列印垃圾回收資訊
//-XX:+HeapDumpOnOutOfMemoryError //OOM Dump
例如:
-Xms1m -Xmx8m -XX:HeapDumpOnOutOfMemoryError
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章