方法區:
1. 存放了要加載的類信息、靜態變量、final類型的常量、屬性和方法信息。唯一元素。
2. JVM用持久代(Permanet Generation)來存放方法區。
3. 是各個線程共享的內存區域。線程公有。
4. 可通過-XX:PermSize和-XX:MaxPermSize來指定最小值和最大值。 默認16m 最大64m。
虛擬機棧:
1. 棧存放的是局部變量和方法調用。每執行一個方法都會在棧中申請一個棧幀,每個棧幀包括局部變量區和操作數棧,用于存放本次方法調用的臨時變量,參數和中間結果。
2. 線程私有的。每個線程都有一個私有的棧。
3. 其大小可以設置為固定大小和動態增長。(編譯期間內存空間分配)
4. 生命周期也可以確定,隨著線程的創建而創建,方法返回,數據就會自動消失。
5.當棧調用深度大于JVM所允許的范圍,會拋出StackOverflowError的錯誤。當那就是當申請不到空間時,會拋出 OutOfMemoryError。?
本地方法棧:
1.? 與虛擬機棧基本類似,區別在于虛擬機棧為虛擬機執行的java方法服務,而本地方法棧則是為Native方法服務。支持native方法的執行,存儲了每個native方法調用的狀態.一個支持native方法調用的jvm實現的數據區
2. 線程私有
堆
1. new出來的對象和數組 只存放對象本身 不存放對象的引用和基本數據類型。
2. 被各個線程共享的內存區域.線程公有?
3. 由垃圾回收器負責回收,生命周期不需要管理。因為其動態的分配內存空間,其存儲速度也相對慢。
4. 堆被劃分為新生代和老年代。(比例為1:3)新生代主要存儲新創建的對象和尚未進入老年代的對象。老年代存儲經過多次新生代GC(Minor GC)任然存活的對象。新生代又分為兩個survior區和一個eden區 比例為(1:1:8)
5.?當申請不到空間時會拋出 OutOfMemoryError
程序計數器(寄存器):
1. 是最小的一塊內存區域? 用來記錄當前線程記錄當前線程正在執行的指令。如果當前正在執行的方法是本地方法,那么此刻程序計數器的值為undefined。
2. 線程私有
3.這是唯一一個不會拋出運行時outofmemory的運行時數據區
運行時常量池:
0.? jdk1.6字符串常量池存在于方法區。 jdk1.7存在于堆區 jdk1.8位于元空間。
1. jvm為了減少字符串對象的重復創建,其維護了一個特殊的內存,這段內存被稱為字符串常量池或字符串字面量池。
2. 工作原理:當代碼中出現字面量形式創建字符串對象時,jvm首先會對這個字面量進行檢查,如果字符串常量池中存在相同內容的字符串對象的引用,則將這個引用返回,否則創建新的字符串對象,并在字符串常量池中開辟新的內存存入,并返回該引用。
3. 字符串常量池存儲的是字符串對象的一個引用。
4. 字符串常量時實現的前提條件就是string對象的不可變。這樣可以安全保證多個變量共享同一個對象。
5. 優點:減少相同內容字符串的創建,節省內存空間。 缺點:以時間換空間 時間是指cpu需要在字符串常量池中查找是否有內容相同的引用,其內部實現為hashtable 計算成本相對較低。
6. intern函數
分析內存模型可以從存放類型角度比較?從生命周期和大小是否可控角度比較 從線程私有公有角度比較 從異常的角度比較。
其他??:字符串是一個特殊包裝類,其引用是存放在棧里的,而對象內容必須根據創建方式不同定(常量池和堆).有的是編譯期就已經創建好,存放在字符串常 量池中,而有的是運行時才被創建.使用new關鍵字,存放在堆中。
關于永久代和元空間
1. 只有hotpot虛擬機 其他的虛擬機沒有永久代。 永久代在之前是位于方法區中。
2. 移除永久代的工作從1.7之后就開始了,在jdk1.7中 存儲在永久代的部分數據就開始轉移到java heap或native heap中,比如字面量就在1.7時到了java heap中,類的靜態變量(class statics)轉移到了java heap,但永久代還存在于1.7中,沒有完全移除。
3. 到了1.8,就不存在永久代這一說法了,取而代之的是元空間(Meta Space)。
4. 元空間,本質和永久代類似,都是jvm規范中方法區的實現,不過元空間和永久代最大的區別是:元空間不在虛擬機中,而是本地內存,因此,默認情況下,元空間的大小受本地內存的限制。
5. 取消永久代的原因:在方法區中容易出現性能問題和內存溢出,類信息的大小很難確定,永久代大小指定困難。永久代為gc帶來復雜度,回收率偏低。