Java記憶體區域
1. 行程與執行緒
行程:行程是程式的一次執行程序,系統運行一個程式就是行程從創建到運行再到消亡的程序,
執行緒:一個行程中包含多個執行緒,執行緒共享行程的堆和方法區的資源以及直接記憶體,同時執行緒私有資源的包括程式計數器、虛擬機堆疊和本地方法堆疊,
2. JVM運行時區域


2.1 程式計數器:
- 位元組碼解釋器通過改變程式計數器的值來一次讀取指令,實作代碼的流程控制:如順序執行、選擇、回圈、例外處理等;
- 多執行緒情況下,程式計數器用于記錄當前執行緒執行的位置,保證當執行緒切換回來時可以繼續執行;
- 程式計數器私有化保證了執行緒切換回來后可以從正確的位置繼續執行,
- 唯一一個不會出現OutOfMemoryError的記憶體區域,生命周期隨執行緒的創建而創建,隨執行緒的結束而死亡,
2.2 Java虛擬機堆疊
虛擬機堆疊描述的是Java方法執行的記憶體模型,生命周期與執行緒相同,每個Java方法在執行時會創建一個堆疊幀用于存放區域變數表、運算元堆疊、動態鏈接、方法出口等資訊,每一個方法被呼叫直至執行完成的程序,就對應著一個堆疊幀在虛擬機堆疊中從入堆疊到出堆疊的程序,
虛擬機堆疊中會出現StackOverFlowError和OutOfMemoryError兩種例外,
2.3 本地方法堆疊
和虛擬機堆疊類似,區別在于虛擬機堆疊為執行Java方法服務,本地方法堆疊則為執行本地方法服務,本地方法堆疊中也會出現StackOverFlowError和OutOfMemoryError兩種例外,
2.4 堆
堆是JVM所管理的記憶體中最大的一塊,是執行緒共享的一塊記憶體區域,在JVM啟動時創建,用于存放物件實體,幾乎所有的物件實體以及陣列都在堆上分配記憶體,
堆是垃圾收集管理的主要區域,也被稱為GC堆,從GC的角度,當前的垃圾收集器都基本采用分代垃圾收集演算法,因此堆可以細分為:新生代、老年代、永久代(JDK1.8被移除,被直接記憶體中的元空間代替),

2.4.1 新生代
新生代用于存放新生的物件,一般占據堆空間的1/3,新生代又細分為Eden、From Survivor以及To Survivor,三者占比為8:1:1,新生代中會頻繁地進行Minor GC操作實作垃圾回收,
- Eden:新創建的物件絕大部分會先分配在Eden區,如果物件太大會直接分配到老年代,當Eden區域記憶體不夠時,就會觸發Minor GC(新生代采用復制演算法),對新生代進行一次垃圾回收,
- From Survivor和To Survivor:在GC開始時,物件只會存在于Eden和From Survivor區,To Survivor為空,在一次Minor GC發生后,仍然存活的物件會移動到To Survivor,其年齡加1,并清空Eden和From Survivor區,當物件的年齡達到一定程度(默認為15),就會進入到老年代,Minor GC完成后,From Survivor和To Survivor功能互換,下一次Minor GC時,會把To Survivor和Eden區存活物件放入From Survivor區中,并計算物件存活的年齡,
2.4.2 老年代
老年代主要存放應用中生命周期長的記憶體物件,老年代比較穩定,不會頻繁地進行Full GC,在進行Full GC前會進行一次Minor GC,當新生物件進入老年代導致記憶體不夠時Full GC才會觸發,當無法找到足夠大的連續空間分配給新創建的較大物件也會提前觸發一次Full GC進行垃圾回收騰出空間,
老年代中Full GC采用標記-整理演算法,Full GC的耗時比較長,因為要掃描再回收,Full GC會產生記憶體碎片,當老年代也沒有記憶體分配給新來的物件的時候,就會拋出OutOfMemoryError例外,
2.4.3 永久代
永久代指的是永久保存區域,主要存放Class和Meta(元資料)的資訊,在JDK1.8中,永久代被元空間代替,元空間不在虛擬機中,而是使用直接記憶體,
采用元空間不使用永久代的原因:
- 為了解決永久代的OOM問題,元資料和class物件存放在永久代中,容易出現性能問題和記憶體溢位,
- 類及方法的資訊等比較難確定其大小,因此對于永久代大小指定比較困難,大小容易出現永久代溢位,太大容易導致老年代溢位(堆記憶體不變,此消彼長),
- 永久代會為GC帶來不必要的復雜度,并且回收效率偏低,
2.5 方法區
方法區與對一樣都是各個執行緒共享的記憶體區域,用于存放已被虛擬機加載的類資訊、常量、靜態變數、即時編譯器編譯后的代碼等資料,
JDK1.8中將方法區移除,取而代之的是元空間,位于直接記憶體中,
方法區是Java虛擬機規范中的定義,是一種規范,永久代是對方法區的一種實作,是HotSpot的概念,
2.6 運行時常量池
運行時常量池是方法區的一部分,用于存放編譯期生成的各種字面量和符號參考,收到方法區記憶體的限制,當常量池無法再申請到記憶體時會拋出OutOfMemoryError例外,
JDK1.7及之后版本將運行時常量池從方法區中一處,在堆中開辟一塊區域存放運行時常量池,
2.7 直接記憶體
直接記憶體不是虛擬機運行時資料區的一部分,也不是虛擬機規范中定義的記憶體區域,但是這部分記憶體也被頻繁使用,也可能導致拋出OutOfMemoryError例外,
JDK1.4中新加入的NIO(同步非阻塞IO),引入了一種基于通道(Channel)和快取區(Buffer)的I/O方式,可以直接使用本地函式庫分配對外記憶體,然后通過存盤在堆中的DirectByteBuffer物件作為這塊記憶體的參考進行操作,這樣就能在一些場景中顯著提高性能,避免了在Java堆和本地堆之間來回復制資料,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/86670.html
標籤:Java
