Java虛擬機:虛擬機內存區域和內存溢出異常

轉載html

[Toc] 字節碼數據在 Java 虛擬機內存中是如何存放的 ?Java 虛擬機在爲類實例或成員變量分配內存是如何分配的 ?要解答上面這些問題,咱們首先須要瞭解一下 Java 虛擬機的內存結構。 其實 Java 虛擬機的內存結構並非官方的說法,在《Java 虛擬機規範》中用的是「運行時數據區」這個術語。但不少時候這個名詞並非很形象,再加上日積月累的習慣,咱們都習慣用虛擬機內存結構這個說法了。 數據結構

根據《Java 虛擬機規範》中的說法,Java 虛擬機的內存結構能夠分爲公有和私有兩部分。公有指的是全部線程都共享的部分,指的是 Java 堆、方法區、常量池。私有指的是每一個線程的私有數據,包括:PC寄存器、Java 虛擬機棧、本地方法棧。jvm

公有部分:Java堆、方法區、常量池

在 Java 虛擬機中,線程共享部分包括 Java 堆、方法區及常量池。 Java 堆指的是從 JVM 劃分出來的一塊區域,這塊區域專門用於 Java 實例對象的內存分配,幾乎全部實例對象都在會這裏進行內存的分配。之因此說幾乎是由於有特殊狀況,有些時候小對象會直接在棧上進行分配,這種現象咱們稱之爲「棧上分配」。這裏並不深刻介紹,後續有章節會介紹。 方法區指的是存儲 Java 類字節碼數據的一塊區域,它存儲了每個類的結構信息,例如運行時常量池、字段和方法數據、構造方法等。能夠看到常量池實際上是存放在方法區中的,但《Java 虛擬機規範》將常量池和方法區放在同一個等級上,這點咱們知曉便可。 方法區在不一樣版本的虛擬機有不一樣的表現形式,例如在 1.7 版本的 HotSpot 虛擬機中,方法區被稱爲永久代(Permanent Space),而在 JDK 1.8 中則被稱之爲 MetaSpace。 說完這幾個部分的大體做用以後,咱們來深刻說說 Java 堆。 Java 堆根據對象存活時間的不一樣,Java 堆還被分爲年輕代、老年代兩個區域,年輕代還被進一步劃分爲 Eden 區、From Survivor 0、To Survivor 1 區。以下圖所示。 學習

當有對象須要分配時,一個對象永遠優先被分配在年輕代的 Eden 區,等到 Eden 區域內存不夠時,Java 虛擬機會啓動垃圾回收。此時 Eden 區中沒有被引用的對象的內存就會被回收,而一些存活時間較長的對象則會進入到老年代。在 JVM 中有一個名爲 -XX:MaxTenuringThreshold 的參數專門用來設置晉升到老年代所須要經歷的 GC 次數,即在年輕代的對象通過了指定次數的 GC 後,將在下次 GC 時進入老年代。 這裏讓咱們思考一個問題:爲何 Java 堆要進行這樣一個區域劃分呢? 根據咱們的經驗,虛擬機中的對象必然有存活時間長的對象,也有存活時間短的對象,這是一個廣泛存在的正態分佈規律。若是咱們將其混在一塊兒,那麼由於存活時間短的對象有不少,那麼勢必致使較爲頻繁的垃圾回收。而垃圾回收時不得不對全部內存都進行掃描,但其實有一部分對象,它們存活時間很長,對他們進行掃描徹底是浪費時間。所以爲了提升垃圾回收效率,分區就理所固然了。 另一個值得咱們思考的問題是:爲何默認的虛擬機配置,Eden:from :to = 8:1:1 呢? 其實這是 IBM 公司根據大量統計得出的結果。根據 IBM 公司對對象存活時間的統計,他們發現 80% 的對象存活時間都很短。因而他們將 Eden 區設置爲年輕代的 80%,這樣能夠減小內存空間的浪費,提升內存空間利用率。url

私有部分:PC寄存器、Java 虛擬機棧、本地方法棧

Java 堆以及方法區的數據是共享的,可是有一些部分則是線程私有的。線程私有部分能夠分爲:PC 寄存器、Java 虛擬機棧、本地方法棧三大部分。 PC 寄存器,顧名思義 Program Counter 寄存器,指的是保存線程當前正在執行的方法。若是這個方法不是 native 方法,那麼 PC 寄存器就保存 Java 虛擬機正在執行的字節碼指令地址。若是是 native 方法,那麼 PC 寄存器保存的值是 undefined。任意時刻,一條 Java 虛擬機線程只會執行一個方法的代碼,而這個被線程執行的方法稱爲該線程的當前方法,其地址被存在 PC 寄存器中。 Java 虛擬機棧,這個棧與線程同時建立,用來存儲棧幀,即存儲局部變量與一些過程結果的地方。棧幀存儲的數據包括:局部變量表、操做數棧。 當 Java 虛擬機使用其餘語言(例如 C 語言)來實現指令集解釋器時,也會使用到本地方法棧。若是 Java 虛擬機不支持 natvie 方法,而且本身也不依賴傳統棧的話,能夠無需支持本地方法棧。spa

總結

Java 虛擬機的內存結構是學習虛擬機所必須掌握的地方,其中以 Java 堆的內存模型最爲重要,由於線上問題不少時候都是 Java 堆出現問題。所以掌握 Java 堆的劃分以及經常使用參數的調整最爲關鍵。 除了上述所說的六大部分以外,其實在 Java 中還有直接內存、棧幀等數據結構。但由於直接內存、棧幀的使用場景還比較少,因此這裏並不作介紹,以避免讓初學者一時間混淆。 學到這裏,一個 Java 文件就加載到內存中了,而且 Java 類信息就會存儲在咱們的方法區中。若是建立對象,那麼對象數據就會存放在 Java 堆中。若是調用方法,就會用到 PC 寄存器、Java 虛擬機棧、本地方法棧等結構。那麼面對如此之多的 Java 類,JVM 是如何決定這些類的加載順序,又是如此控制它們的加載的呢?下一節,咱們講講 JVM 的類加載機制。 .net

相關文章
相關標籤/搜索