Java虛擬機--自動內存管理機制

PS :https://blog.csdn.net/column/details/java-vm.html

Java虛擬機內存模型

這裏寫圖片描述
在這裏插入圖片描述

  • 虛擬機棧:Java方法的內存模型,即每個方法的執行都會創建一個虛擬機棧幀,方法的執行過程就是棧幀的入棧出棧,每個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法返回地址和一些額外的附加信息(運行期會有JIT優化,但我們理論上認爲這部分所需內存編譯期可知並寫入了方法表);線程獨立;StackOverflowError和OutOfMemoryError
    • 局部變量表:存儲編譯期可知的基本數據類型、對象引用reference、返回值類型returnAddress(它指向了一條字節碼指令的地址);局部變量表的空間在java編譯成class文件時就已經確定其最大容量,運行期間不會改變;局部變量表是可以複用的,當指令執行超過某變量的作用範圍則該指令佔有的slot(變量槽,描述每個變量佔用空間的最小單位,long、double可能需要兩個slot來表示)可以被重用。局部變量表的變量並不會賦予初始值,直接使用會報錯。
    • 操作數棧:方法開始時爲空,執行過程中將各種操作入棧出棧(即執行iadd等指令)。編譯成class時確定棧大小,java虛擬機是基於操作數棧的,執行速度相對慢,但可移植性強;Android虛擬機是基於寄存器的,執行速度快,移植性差
    • 動態鏈接:每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是爲了支持方法調用過程中的動態鏈接。
    • 方法返回地址:正常時,調用者的PC計數器的值作爲返回地址(把返回值壓入調用者的操作數棧中,調整PC計數器的值以指向方法調用指令後面的一條指令);異常退出則由異常處理器決定
  • 本地方法棧:native方法的內存模型,與虛擬機棧雷同。
  • Java堆:存放對象實例和數組;線程共享;Java垃圾收集器的主要對象,根據垃圾收集的需要還可分爲老年代、新生代等。可能劃分出多個線程私有的分配緩衝區。OutOfMemoryError
  • 方法區:存儲虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等,線程共享;這個內存區可不實現垃圾回收,若實現主要回收廢棄的常量(如方法區中有“abc”常量,當前系統無String對象引用“abc”即可回收)和無用類(滿足下列條件:1.堆中不存在實例2.加載該類的ClassLoader被回收3.類對應的Class對象沒有引用,無法通過反射加載);OutOfMemoryError
    • 運行時常量池:方法區的一部分,用於存放編譯期生成的各種字面量和符號引用;
  • 程序計數器:當前線程(線程獨立)所執行的虛擬機字節碼的行號指示器。即正在執行的虛擬機字節碼指令的地址。執行Java代碼時指向字節碼地址,執行Native方法則爲空,不會OOM。(程序計數器的作用在於多線程,單線程無需程序計數器也可以根據字節碼的跳轉命令順利執行,多線程中需要掛起恢復時才需要知道之前執行到什麼位置)
  • 直接內存:非虛擬機規範內容,受本機總內存的大小及處理器尋址空間限制;OutOfMemoryError

PS:不用對象置null的意義
這個其實主要在於局部變量表的slot可以被複用機制產生。

{
	byte[] placeholder = new byte[64 * 1024 * 1024];
}
//int a = 0;  或者 placeholder = null;
System.gc();

上述代碼,剛出了placeholder作用範圍馬上GC過來回收垃圾,此時placeholder並不會被回收,因爲這塊空間沒有需要被複用,GC並不會回收它。而如果把註釋打開,則因爲需要填充新的變量,GC就會把placeholder佔用的空間回收填充新的變量了。

JVM和DVM的區別?

垃圾回收機制

虛擬機棧、本地方法棧、程序計數器中的數據隨線程的生命而創建回收,隨方法的入棧出棧而創建回收,無需垃圾回收機制。垃圾回收機制的重點在Java堆和方法區

對象是否已經無用判斷?

1.引用計數法:每個對象有一個引用計數器來記錄當前引用數,爲0則可回收。缺陷:很難解決對象之間互相循環引用的問題
2.可達性分析法:GC Roots枚舉根節點(一個集合)沒有引用鏈相連的對象就是不可用對象。GC Roots的集合對象包括:①虛擬機棧(棧幀中本地變量表)中引用的對象②方法區中類靜態屬性引用的對象③方法區中常量引用的對象④本地方法棧中JNI引用的對象

對象的回收

GC Roots不可達則標記對象;判斷是否執行finalize()方法(對象沒有覆蓋finalize方法或finalize已經被虛擬機調用過則不執行),若需要執行,則執行後再次判斷對象是否GC Roots可達,還是不可達則回收(可在finalize中與GC Roots對象關聯)

GC Roots枚舉根節點集合的維護

代碼執行過程中有符合條件的對象都需要加入到GC Roots集合中,且獲取時需要暫停整個系統,否則分析結果無法得到保障。通常,代碼執行到安全點纔會停下來維護GC Roots枚舉(主動式中斷:GC設置一個標誌,各線程執行時執行到安全點會去輪詢,主動掛起,維護GC Roots枚舉)

垃圾回收算法

  • 標記清除算法:產生大量碎片
  • 標記整理算法:耗時,有連續空間
  • 複製算法:浪費內存空間
  • 分代收集算法:年輕代(空間較小,垃圾收集頻繁而快速)、老年代(空間較大,垃圾收集較少,一般採用標記整理法,較爲耗時)

JDK1.7中垃圾回收器

  • Serial (串行)收集器
    這裏寫圖片描述
  • ParNew
    與Serial類似,只不過新生代GC線程是多線程而已
  • Parallel
    與Serial類似,只不過新生代GC線程和老年代GC線程都是多線程而已
    這裏寫圖片描述
  • CMS (優點:併發收集、低停頓 缺點:對CPU資源非常敏感,無法處理浮動垃圾,產生大量碎片)
    ①初始標記(CMS initial mark) 標記GC Roots能直接關聯到的對象;阻塞,耗時短
    ②併發標記(CMS concurrenr mark) 標記回收對象;併發
    ③重新標記(CMS remark) 標記因用戶運行產生的可回收對象;阻塞
    ④併發清除(CMS concurrent sweep) 清理;併發
    這裏寫圖片描述
  • G1
    ①初始標記(Initial Marking)
    ②併發標記(Concurrent Marking)
    ③最終標記(Final Marking)
    ④篩選回收(Live Data Counting and Evacuation)
    這裏寫圖片描述

Java虛擬機相關配置

  • -Xmx –Xms:指定最大堆和最小堆
  • -Xmn 設置新生代大小
  • -XX:NewRatio 新生代(eden+2*s)和老年代(不包含永久區)的比值。例如:4,表示新生代:老年代=1:4,即新生代佔整個堆的1/5
  • -XX:SurvivorRatio 設置兩個Survivor區和eden的比值。例如:8,表示兩個Survivor:eden=2:8,即一個Survivor佔年輕代的1/10
  • -XX:+HeapDumpOnOutOfMemoryError OOM時導出堆到文件,根據這個文件,我們可以看到系統dump時發生了什麼
  • -XX:+HeapDumpPath 導出OOM的路徑
  • -XX:OnOutOfMemoryError 在OOM時,執行一個腳本。 可以在OOM時,發送郵件,甚至是重啓程序
  • -XX:PermSize -XX:MaxPermSize 設置方法區的初始空間和最大空間
  • -Xss 設置棧空間的大小

成員變量存在於堆內存中,局部變量存在於棧內存中,靜態變量存在於方法區中

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章