天天看點

java JVM記憶體模型與記憶體溢出異常

1 前言

聲明:本文參考了深入了解Java虛拟機:JVM進階特性與最佳實踐 第2版,可作為該書的一個讀書筆記

2 jvm 記憶體模型

java 虛拟機在運作時,記憶體模型如下:

java JVM記憶體模型與記憶體溢出異常

可以看到,虛拟機棧,本地方法棧,程式計數器是線程私有的,随着線程的消亡而消亡,生命周期與線程相同。而方法區,堆的生命周期是和jvm(一個jvm執行個體就是一個程序)一起的,随着jvm的消亡而消亡。

下面分别介紹各個區域

程式計數器

程式計數器是一塊較小的記憶體空間,可以看做是目前線程所執行的位元組碼的行号訓示器。因為java虛拟機的多線程是輪流切換并配置設定處理器資源的,是以每個線程都有一個獨立的程式計數器。是以該區域不是記憶體共享的。

另外,如果執行的是Native方法,這個計數器的值為null,并且該區域是jvm中唯一一個不會發生OutOfMemoryError的區域

虛拟機棧

與程式計數器一樣,虛拟機棧也是線程私有的。每個方法在執行的時候會建立一個棧幀,用于存儲局部變量表,操作數棧,動态連結,方法出口等資訊。方法的執行對應着方法棧的入棧和出棧過程。

java JVM記憶體模型與記憶體溢出異常

本地方法棧

本地方法棧與虛拟機棧的作用很相似,差別是本地方法棧是為執行本地方法服務的。有的虛拟機如HotSpot直接将本地方法棧與虛拟機棧合二為一。

本地方法棧也會抛出與虛拟機棧一樣的StackOverflowError和OutOfMemoryError異常

java堆

java堆是所有線程共享的一塊記憶體區域,由虛拟機啟動時建立,堆得唯一目的就是存儲對象的執行個體。幾乎所有的對象執行個體都在改記憶體區域配置設定(但是随着JIT的發展及逃逸分析技術的發展,導緻發生了一些不一定)。

java堆是垃圾收集器管理的主要區域。根據現在GC技術采用的算法(分代收集算法),現在java堆一般分為新生代和老生代。

新生代:主要存放應用程式中生命周期短的記憶體對象,經常被回收

老生代:主要存放應用程式中生命周期長的記憶體對象

另外,從記憶體配置設定的角度來看,堆中可能劃分多個線程私有的配置設定緩沖區(TLAB)。

另外,java堆可以是處于實體上不連續的記憶體空間,隻要邏輯上連續即可。通過-Xmx,-Xms來控制堆的擴充。

如果堆無法擴充,将會抛出OutOfMemoryError異常

方法區

方法區也是多個線程共享的區域。它主要存儲已被虛拟機加載的類資訊(包括類中定義的方法),常量,靜态變量,即時編譯器優化後的代碼等資料。

對于HotSpot虛拟機來說,有時方法區又被稱為“永久代”,這是因為HotSpot選擇把垃圾收集擴充至方法區。

當方法區無法再配置設定擴充記憶體時,會抛出OutOfMemoryError異常

另外,方法區中包含運作時常量池運,是方法區的一部分,主要存儲編譯器期間生成的各種字面量及符号引用。

直接記憶體

直接記憶體并不是虛拟機的一部分,也不是java虛拟機規範中定義的記憶體區域,但是這部分記憶體在java程式中也經常被用到。也有可能導緻OutOfMemoryError異常的出現。

例如:java程式中通過NIO(New Input/Output),通過native函數直接配置設定的記憶體就屬于直接記憶體。

直接記憶體并不受java堆大小的限制,隻是受本機實體記憶體的限制。

3 jvm記憶體溢出異常

在時機開發中,對于現在的虛拟機,往往可以配置該虛拟機的各個部分記憶體的擴充,下面來介紹一下配置的參數以及對應的記憶體溢出。

虛拟機棧與本地方法棧

對于HotSpot來說,這二者是合二為一的。

-Xss:設定棧容量大小 -Xss100MB

一般棧有下面兩種異常

1 線程請求的棧深度大于虛拟機允許的深度,将抛出StackOverflowError異常

2 如果虛拟機允許擴充,當無法申請到足夠的記憶體時,将抛出OutOfMemoryError

一般在單線程中,當我們定義大量本地變量,或者減少棧記憶體容量,抛出的都是StackOverflowError異常,實驗不出OutOfMemoryError異常

在多線程中,通過不斷的建立線程倒是可以抛出OutOfMemoryError異常。

發生異常的關鍵字如下:

error: java.lang.StackOverflowError
[INFO]  at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:)
           

java堆

-Xms:設定虛拟機堆的最小值 -Xms1000MB

-Xmx: 設定虛拟機堆的最大值 -Xmx1500MB

通過-Xmx,-Xms來控制堆的擴充。如果堆無法擴充,将會抛出OutOfMemoryError異常

一般提示資訊有這樣一句代碼:

方法區

使用-XX:permSize與-XX:MaxPermSize來限定方法區的大小

當方法區記憶體不足時,将會發生OutOfMemoryError異常

異常資訊如下:

直接記憶體溢出

-XX:MaxDirectMemorySize:指定直接記憶體大小,例如:-XX:MaxDirectMemorySize=100MB

當直接記憶體溢出時,Heap Dump檔案不會看到明顯的異常,如果發現OOM之後的dump檔案很小,而程式中使用了NIO,就要考慮直接記憶體的溢出了。

繼續閱讀