1. 程式人生 > >深入理解java虛擬機器閱讀筆記二

深入理解java虛擬機器閱讀筆記二

三 垃圾收集器和記憶體分配策略

3.1 垃圾收集(Garbage Collection,GC)和記憶體動態分配最早於1960年的MIT開發的Lisp語言中使用

需要解決的三個問題:

  • 回收哪些記憶體
  • 什麼時候回收
  • 怎麼回收

程式計數器,虛擬機器棧,本地方法棧3個區域隨執行緒生死,棧中的每個棧幀分配多少記憶體已經在類結構確定的時候已知,因此這幾個區域的記憶體分配和回收都具確定性

而java堆和方法區則不同,同一個介面的不同實現類的記憶體可能不一樣,我們只有在執行期才能知道建立哪些物件,當然這些物件的記憶體空間分配和回收也是動態的,垃圾收集器關注的是這部分記憶體

儘管目前記憶體動態分配和垃圾回收已經非常成熟在技術方面,但是因為需要排除各種記憶體溢位,記憶體洩漏問題,同時系統需要更高併發量而垃圾收集是提高這個併發量手段時候,我們需要對這個自動化的記憶體分配和記憶體回收進行監控調節優化

3.2 物件是否存活的判斷演算法

堆中存放著java中所有的物件例項,垃圾收集器回收堆中物件的時候,必須判斷物件是否還存活,即物件是否可達。

判斷物件是否存活的演算法有以下兩種

  • 引用計數器演算法:道理很簡單,給物件裡面新增一個引用計數器,物件被引用一次,計數器+1,引用失效一次,計數器-1,當計數器為0的時候說明物件是不可能被使用的,著名的Python語言就使用的該演算法。但是很可惜,java虛擬機器並沒有使用該演算法管理記憶體,最主要原因:難以解決物件之間相互引用所造成的問題
  • 可達性分析演算法 reachability analysis 通過一系列GC Roots的物件作為起始點,然後向下搜尋,搜尋走過的路徑叫做引用鏈,當物件和GC root's沒有任何引用鏈條相連的話,該物件即不可用,儘管下圖中物件5,6,7互相關聯,但是和根節點GC Roots沒有連線,即不可達,所有5,6,7節點是判定可回收物件

java中,可作為GC Roots的物件有

  • 虛擬機器棧引用的物件
  • 方法區中類靜態成員引用的物件
  • 方法區中常量引用的物件
  • 本地方法棧中引用的物件

物件的引用分4種

  • 強引用:類似Object o = new Object();只要強引用存在,垃圾收集器永遠不回收被引用物件
  • 軟引用:描述還有用但是非必須的物件,如果系統將要記憶體溢位,則在第二次垃圾回收的時候回收這些物件,當回收以後還沒足夠記憶體,就丟擲記憶體溢位exception
  • 弱引用:比軟引用強度更低,只要收集器開始工作,在第二次垃圾回收之前就會將被弱引用關聯的物件回收
  • 虛引用:最弱的引用關係,一個物件的生存時間和是否具有虛引用無關,即虛引用不存在了,物件仍然可能存在,當然,也無法通過虛引用取得一個物件例項,為物件設定虛引用關聯的唯一目的就是該物件在被回收的時候可以收到一個系統通知

3.3 可達性分析演算法種不可達的物件是否一定死亡?

不一定,當發現某個物件不可達的時候,會對他進行第一次標記,看是否執行finalize方法,當物件沒有覆蓋finalize方法或者finalize方法已經被vm呼叫,則沒必要執行

如果執行,那麼他會被加入F-queue佇列,然後啟動一個後臺執行緒去執行他,不保證會執行完,加入佇列以後過一會將會進行第二次標記,如果物件在finalize中成功拯救了自己,即將this賦值給某個成員變數,則第二次標記的時候將會把他移出即將回收的集合,如果沒有逃脫,那麼這個物件就真的被回收了,程式碼如下

public class JVMt {

	static JVMt jvmt = null;
	
	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		System.out.println("enter finalize method");
		JVMt.jvmt = this;
	}

	public static void main(String[] args) throws InterruptedException {
		jvmt = new JVMt();
		
		// 讓它不可達
		jvmt = null;
		
		// 啟動GC
		System.gc();
		
		// finalize優先順序低,需要等待時間讓他執行
		Thread.sleep(500);
		
		if(jvmt!=null) {
			System.out.println("I am stil alive");
		}else {
			System.out.println("I am dead");
		}
		
		// 讓它不可達
		jvmt = null;
		
		// 啟動GC
		System.gc();
		
		// finalize優先順序低,需要等待時間讓他執行
		Thread.sleep(500);
				
		if(jvmt!=null) {
			System.out.println("I am stil alive");
		}else {
			System.out.println("I am dead");
		}
	}
	
}

結果:

enter finalize method
I am stil alive
I am dead

3.4 GC對於方法區的回收

主要兩部分

  • 廢棄常量的回收:比如abc,常量池中沒有任何一個String物件是abc

  • 類的回收:需要滿足3個條件

    1. java堆中該類的例項已經被完全回收完

    2. 該類的類載入器已經被回收

    3. 該類的 Class物件沒有引用,無法在任何地方通過反射訪問該類

3.5 垃圾收集演算法

  • 標記清理演算法Mark-sweep:最基本的演算法,標記需要回收的物件,然後標記完成後對這些物件進行統一回收,有兩個缺點:

    1. 效率不高:標記的過程和清理的過程效率都不高

    2. 容易產生大量不連續的記憶體碎片,如果碎片太多,以後如果要分配較大物件的時候,使得無法找到足夠大的連續記憶體而提前觸發下一次GC

  • 複製演算法Copying:目前商用虛擬機器對於新生代收集最流行演算法,原理是將堆中可用記憶體按照容量大小平均的1:1劃分為2部分,每次只使用1部分,使用的這一部分中物件可分為3個組成塊(可以回收,存活物件,空閒記憶體),當這一部分中的所有記憶體用完以後,即可以回收+存活物件所佔記憶體=區間記憶體------------>就將這塊還存活的物件複製到另一部分中,然後堆這部分中的可以回收進行GC

    優點:高效:只需移動指標,不需考慮記憶體碎片化等複雜情況

    確定:浪費記憶體,每次只是用一部分記憶體

    HotSpot虛擬機器使用的GC演算法就是這個,將堆分為3部分,Eden,from survivor,to survivor,三者的比例是8:1:1,其中Eden和from survivor主要用於存放物件,回收的時候就將這兩塊的物件一次性複製到to survivor記憶體中,如果to survivor空間容量不足以存放上一次新生代收集的存活物件的時候,這些物件直接通過分配擔保機制進入老年代

  • 標記整理演算法:考慮到複製演算法,如果存活的物件數量龐大,在記憶體滿以後進行復制效率無疑下降太多,同時如果不想浪費剩下的50%記憶體,就需要額外空間分配擔保,以避免存活物件100%的極端情況,因此在老年代一般不用這種

    老年代選用標記整理演算法,和標記清理演算法很類似,就是所有物件標記完成以後先不清理,而是先將存活物件移動到邊界整理,然後直接清理邊界以外的記憶體

  • 分代收集演算法Generational Collection:當前商業虛擬機器最流行演算法,根據物件的存活 週期不同分為新生代和老年代,新生代每次都有大量物件死亡,少量存活,採用複製演算法,老年代因為物件存活率高,沒有額外空間使用標記整理/標記清除演算法


相關推薦

深入理解java虛擬機器閱讀筆記

三 垃圾收集器和記憶體分配策略3.1 垃圾收集(Garbage Collection,GC)和記憶體動態分配最早於1960年的MIT開發的Lisp語言中使用需要解決的三個問題:回收哪些記憶體什麼時候回收

深入理解java虛擬機器閱讀筆記)物件是否存活與垃圾收集演算法

1.1  判斷物件是否存活 1.1.1  引用計數演算法:給每個物件新增一個引用計數器,當一個地方引用此物件時,該計數器值+1;當引用失效時,該計數器值-1;當此物件沒有被引用時,該計數器的值為0。雖然此演算法實現簡單,效率高,但是很難解決兩個物件之間相互迴圈引用的問題。 1.1.2&

深入理解java虛擬機器閱讀筆記(一)java記憶體區域

1.1  概述 對於java來說,虛擬機器是採用的自動管理記憶體機制,不需要手動去寫delete/free程式碼,但是常在河邊走哪有不溼鞋,程式不可避免會遇到記憶體溢位或洩漏的問題,因此知道記憶體區域分佈情況對於記憶體管理是很有必要的。 1.2  執行時資料區域 java虛擬機器在執

讀書筆記深入理解Java虛擬機器》 ()物件建立、記憶體佈局、訪問定位

物件的建立 類載入檢查 檢查這個指令的引數是否能在常量池中定位到一個類的引用 檢查這個符號引用代表的類是否已被載入、解析和初始化過, 如果沒有,那必須先執行相應的類載入過程 確定物件所需記憶體的大小 為新生物件分配記憶體 初始化物件的欄位, 大

深入理解JAVA虛擬閱讀筆記——垃圾回收器

ont 分享 root 深入理解 .com 筆記 直接 用戶線程 另一個 一、垃圾收集器總覽 新生代:Serial、 ParNew、 Parallel Scavenge 老年代:CMS、Serial Old、 Parallel Old 最新的:G1 並行和並發的區別:

深入理解JAVA虛擬閱讀筆記——虛擬機類加載機制

info 程序 動態 alt 訪問 什麽 hand jdk 靜態方法   虛擬機把描述類的Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。 在Java語言中,類型的加載、連接

深入理解java虛擬機器學習筆記(一)

Java記憶體區域模型 Java虛擬機器在執行Java程式的過程中,會把它所管理的記憶體區域劃分為若干個不同的資料區域,這些區域一般被稱為執行時資料區(Runtime Data Area),也就是我們常說的JVM記憶體。 執行時資料區通常包括以下這幾個部分: 程式計數器(Program Counte

深入理解 Java 虛擬機器(十Java 記憶體模型與執行緒

執行緒安全 Java 語言中的執行緒安全 根據執行緒安全的強度排序,Java 語言中各種操作共享的資料可以分為 5 類:不可變、絕對執行緒安全、相對執行緒安全、執行緒相容、執行緒對立。 不可變 不可變的物件一定是執行緒安全的,如果共享資料是一個基本資料型別,那麼

深入理解Java虛擬機器讀書筆記1----Java記憶體區域與HotSpot虛擬機器物件

一 Java記憶體區域與HotSpot虛擬機器物件 1 Java技術體系、JDK、JRE?     Java技術體系包括:         · Java程式設計語言;   

深入理解Java虛擬機器讀書筆記2----垃圾收集器與記憶體分配策略

二 垃圾收集器與記憶體分配策略 1 JVM中哪些記憶體需要回收?     JVM垃圾回收主要關注的是Java堆和方法區這兩個區域;而程式計數器、虛擬機器棧、本地方法棧這3個區域隨執行緒而生,隨執行緒而滅,隨著方法結束或者執行緒結束記憶體自然

Java虛擬機器:執行時資料區域-《深入理解Java虛擬機器筆記

以下內容為周志明《深入理解Java虛擬機器》中的內容摘要。寫本文來作為學習筆記。 Java 虛擬機器在執行時會將管理的記憶體劃分成若干個區域,這些區域各有各的用途,以及各自的建立和銷燬的時間。 有些隨著

深入理解JAVA虛擬機器學習筆記(一)JVM記憶體模型

一、JVM記憶體模型概述 JVM記憶體模型其實也挺簡單的,這裡先提2個知識點: 1、組成:java堆,java棧(即虛擬機器棧),本地方法棧,方法區和程式計數器。 2、是否共享:其中方法區和堆區是執行緒共享的,虛擬機器棧,本地方法棧和程式計數器是執行緒私有的,也稱執行緒

深入理解Java虛擬機器學習筆記2-JVM中物件佈局

一、物件的記憶體佈局 在Hotspot中,物件在記憶體中儲存佈局可以分為三塊: 物件頭(Header) 、例項資料(Instance Data)、對齊填充(Padding) 二、物件頭 1、物件執行時資料: 雜湊嗎、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒I

深入理解Java虛擬機器學習筆記3-執行緒安全和鎖優化

併發處理是壓榨計算機運算能力最有力的工具。 1.執行緒安全 當多個執行緒訪問一個物件時,如果不用考慮這些執行緒執行時環境下排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲取正確的結果,那麼這個物件是執行緒安全的。 2

深入理解Java虛擬機器讀書筆記7----晚期(執行期)優化

七 晚期(執行期)優化 1 即時編譯器(JIT編譯器)     ---當虛擬機發現某個方法或程式碼塊的執行特別頻繁時,就會把這些程式碼認定為“熱點程式碼”,包括被多次呼叫的方法和被多次執行的迴圈體。     ---為了提高熱點程式碼的執行效率

深入理解Java虛擬機器讀書筆記8----Java記憶體模型與執行緒

八 Java記憶體模型與執行緒   1 Java記憶體模型     ---主要目標:定義程式中各個變數的訪問規則,即在虛擬機器中將變數儲存到記憶體和從記憶體中取出變數這樣的底層細節。     ---此處的變數和Java中的變

深入理解Java虛擬機器讀書筆記9----執行緒完全與鎖優化

九 執行緒完全與鎖優化   1 Java語言中的執行緒完全         ---執行緒安全:當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行時環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其

深入理解java虛擬機器學習筆記(六)

第八章 虛擬機器位元組碼執行引擎 8.1概述 執行引擎是Java虛擬機器最核心的組成部分之一。“虛擬機器”是一個相對於“物理機”的概念,這兩種機器都有執行程式碼的能力,其區別是物理機的執行引擎是直接建立在處理器、硬體、指令集和作業系統層面上的,而虛擬機器的執行引擎則是由自己

深入理解Java虛擬機器讀書筆記(3): JVM引數型別分類

深入理解Java虛擬機器讀書筆記(3): JVM引數型別分類 JVM有很多引數,一般可以分為三大類:標準引數、X引數和XX引數 標準引數 所謂標準引數,即一般化引數,往往是固定不變的,比如以下引數: -help -version 顯式虛擬機器型別 當前

深入理解Java虛擬機器讀書筆記(2): 深入理解HotSpot虛擬機器物件

深入理解Java虛擬機器讀書筆記(2): 深入理解HotSpot虛擬機器物件 為了理解虛擬機器中資料的細節,比如如何建立、如何佈局以及如何訪問,必須具體到某一虛擬機器和某一個記憶體區域。此處深入探討HotSpot虛擬機器在Java堆中物件分配、佈局和訪問的全過程。 一、物件的建立