天天看點

《深入了解java虛拟機》---記憶體管理(2)

java和C、C++之間素來有一堵牆,這堵牆就是記憶體動态配置設定和垃圾收集技術。java的記憶體配置設定如下圖

《深入了解java虛拟機》---記憶體管理(2)

程式計數器:目前線程所執行的位元組碼的行号訓示器,是一塊虛拟的很小的記憶體空間,線程私有的

java虛拟機棧:線程私有的,生命周期與線程相同,裡面存放的是基本資料類型,對象引用類型(reference類型,但是他們不是對象本身,一般是指向對象起始位置的引用指針,也有可能是指向一個代表對象的句柄或者其他與此對象相關的位置)和returnAddress類型(指向了一條位元組碼指令的位址)等,其中64為長度的long和double類型會占用兩個局部空間變量,其餘的都是一個,這個區域規定了兩種異常情況:如果線程請求的棧深度大于 虛拟機所允許的深度,就會抛出Stack OverflowError異常;如果虛拟機可以動态擴充,在擴充時沒有申請到足夠的記憶體,就會抛出OutOfMemoryError異常。

本地方法棧:與虛拟機棧發揮的作用是相似的,不同的是隻為本地Native方法服務,抛出異常與虛拟機棧一樣,像Sun HotSpot虛拟機會直接把虛拟機棧和本地方法棧合二為一。

java堆:對于大多數應用來說,java堆是java虛拟機中管理記憶體的最大一塊,是所有線程共享的一塊區域,在虛拟機啟動的時候建立,存放對象執行個體,也就是說所有的對象執行個體以及數組都要在堆上配置設定記憶體,但是随着JIT編譯器的發展和逃逸分析技術的逐漸成熟,未來将會導緻一些微妙的變化,是以現在說所有的對象都是在堆上配置設定記憶體的就有點絕對了。既然管理着對象執行個體,那麼垃圾回收肯定就是這個區域,是以有的時候這個堆也稱為GC堆,為了友善GC線程的回收,java堆中的對象會别分為:新生代和老年代,或者更加細緻的劃分。在java虛拟機的規範中規定,java堆可以處于實體上不連續的記憶體空間中,隻要邏輯上的連續即可,就像是我們的磁盤空間一樣可以是固定大小的,也可以是可擴充的,不過目前主流的虛拟機都是可擴充的(通過配置-Xmx和-Xms參數實作),當堆中沒有記憶體執行個體配置設定,并且也無法擴充時,就會抛出OutOfMemoryError異常。

方法區:和堆一樣是線程的共享區域,主要存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料。對于HotSpot虛拟機把GC分代收集擴充至方法區,這樣更多人願意将方法區稱為“永久代”(PermGen),是以GC也在管理着方法區,采用永久代實作方法區,但是永久代有一個-XX:MaxPermSize的上限,是以更有可能會導緻記憶體溢出的現象發生,是以後來HotSpot逐漸放棄永久代改成Native Memory來實作方法區。其實對于方法區中回收隻是針對常量池的回收和對類型的解除安裝,對于類型的解除安裝條件比較苛刻,但是如果為完全回收會導緻記憶體洩漏問題,未來需要進一步的優化,對于常量池的回收,現在JDK1.7的HotSpot中已經把原本的永久代的字元串常量池移出。當方法區無法滿足記憶體配置設定需求時,就會抛出OutOfMemoryError異常。

運作時常量池:是方法區的一部分,Class檔案中除了類的版本、字段、方法、接口等描述資訊外,還有一項資訊就是常量池,用于存放在編譯器生成的各種字面量和符号引用。是方法區的一部分,也受方法區記憶體的限制,當常量池無法再申請到記憶體的時候就會抛出OutOfMemoryError異常。

直接記憶體:主要作用就是為了提高性能,在NIO類中可以直接使用Native函數配置設定堆外記憶體供外界調用,既然是記憶體就會受到本機總記憶體的限制,可能會出現OutOfMemoryError異常。