日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務,提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

第一章 走進JAVA

java的優(yōu)點:擺脫了硬件平臺的束縛,實現(xiàn)了“一次編寫,到處運行”;它提供了一個相對安全的內(nèi)存管理和訪問機制,避免了絕大部分的內(nèi)存泄漏和指針越界問題;它實現(xiàn)了熱點代碼檢測和運行時編譯及優(yōu)化,這使得java應用能隨著運行時間的增加而獲得更高的性能。

1 java虛擬機發(fā)展史

1.1 Sun Classic/Exact VM(jdk1.0~jdk1.2)

世界上第一款商用java虛擬機,它只能使用純解釋器方式來執(zhí)行Java代碼,如果要使用JIT編譯器,就必須進行外掛,但是外掛JIT后,JIT編譯器就會完全接管虛擬機的執(zhí)行系統(tǒng),解釋器就不工作了。由于Classic VM不能和JIT配合工作,這就意味著如果要使用編譯器執(zhí)行,編譯器就不得不對每一個方法、每一行代碼都進行編譯,而無論他們執(zhí)行的頻率是否具有編譯的價值。基于程序響應時間的壓力,這些編譯器不敢用編譯耗時稍高的優(yōu)化技術(shù) ,即使用了JIT,其執(zhí)行效率也和C++有很大差距。

1.2 Sun HotSpot VM

HotSpot VM繼承了Sun之前兩款商用虛擬機優(yōu)點,HotSpot指是的它的熱點代碼探測技術(shù)。HotSpot VM的熱點代碼探測技術(shù)可以通過執(zhí)行計數(shù)器找出最有編譯價值的代碼,然后通知JIT編譯器以方法為單位進行編譯,如果一個方法被頻繁調(diào)用,或方法中的有效循環(huán)次數(shù)很多,將會分別觸發(fā)標準編譯和OSR編譯動作。通過編譯與解釋器恰當?shù)膮f(xié)同工作,可以在最優(yōu)化的程序響應時間和最佳執(zhí)行性能取得平衡,而且無需等待本地代碼輸出才能執(zhí)行程序,即時編譯的時間壓力也相對減小,這樣有助于更多的代碼優(yōu)化技術(shù)秘輸出質(zhì)量更高的本地代碼。

2 模塊化

它是解決應用系統(tǒng)與技術(shù)平臺越來越復雜、越來越龐大 的一個重要途徑。

3 64位虛擬機

幾年之前,java程序運行在64位虛擬機上需要付出比較大的額外代價:首先是內(nèi)存問題,由于指針和各種數(shù)據(jù)類型對齊補白的原因,運行于64位系統(tǒng)上的java應用程序需要耗費更多的內(nèi)存,通常要比32位系統(tǒng)額外增加10%~30%的內(nèi)存消耗;其次,64位虛擬機性能也全面落后于32位。

第二章 Java內(nèi)存區(qū)域與內(nèi)存溢出異常

2.1 概述

對于java程序員來說,在虛擬機自動內(nèi)存管理機制下,不再需要為每一個new操作,去寫配對的delete/free代碼,不容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出問題,由java虛擬機管理內(nèi)存。但是也正是java程序員把內(nèi)存控制權(quán)交給了java虛擬機,一旦出現(xiàn)內(nèi)存泄漏和溢出方面的問題,不了解虛擬機是怎樣實用內(nèi)存的,那么排查錯誤會是一項艱難的工作。

2.2運行時數(shù)據(jù)區(qū)域

java虛擬機-jvm內(nèi)存回收算法篇

 

程序計數(shù)器(線程私有內(nèi)存區(qū)域): 程序計數(shù)器(Program Counter Register) 是一塊較小的內(nèi)存空間,它可以看作是當前線程所執(zhí)行的字節(jié)碼的行號指示器。在虛擬機的概念模型里,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條執(zhí)行字節(jié)碼指令。每條線程都有一個獨立的程序計數(shù)器,獨立存儲,互不影響,因此這類內(nèi)存區(qū)域被稱為線程私有的內(nèi)存。 如果執(zhí)行的是java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令地址。如果是native方法,計數(shù)器為空。此內(nèi)存區(qū)域是唯一一個在java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

Java虛擬機棧(線程私有內(nèi)存區(qū)域):與程序計數(shù)器一樣,它也是線程私有的,它的生命周期和線程相同。同樣是線程私有,描述Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。一個方法對應一個棧幀。在java虛擬機規(guī)范中,對這個區(qū)域規(guī)定了兩種異常情況:如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError異常;如果虛擬機可以動態(tài)擴展,如果擴展時無法申請到足夠的內(nèi)存,則會拋出OutOfMemoryError異常。

本地方法棧(Native Method Stack):和Java虛擬機棧很類似,虛擬機棧為虛擬機執(zhí)行java方法,不同的是本地方法棧為Native方法服務。與虛擬機棧一樣,本地方法棧也會拋出StackOverflowError異常和OutOfMemoryError異常。

Java堆:是Java虛擬機所管理的內(nèi)存中最大的一塊。由所有線程共享,在虛擬機啟動時創(chuàng)建。堆區(qū)唯一目的就是存放對象實例。堆中可細分為新生代和老年代,再細分可分為Eden空間、From Survivor空間、To Survivor空間。 堆無法擴展時,拋出OutOfMemoryError異常。

方法區(qū)(Non-Heap):所有線程共享,存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。當方法區(qū)無法滿足內(nèi)存分配需求時,拋出OutOfMemoryError.

運行時常量池:它是方法區(qū)的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項是常量池(Const Pool Table),用于存放編譯期生成的各種字面量和符號引用。并非預置入Class文件中常量池的內(nèi)容才進入方法運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發(fā)人員利用得比較多的便是String類的intern()方法。當方法區(qū)無法滿足內(nèi)存分配需求時,拋出OutOfMemoryError。

直接內(nèi)存:并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域。JDK1.4加入了NIO,引入一種基于通道與緩沖區(qū)的I/O方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。因為避免了在Java堆和Native堆中來回復制數(shù)據(jù),提高了性能。 當各個內(nèi)存區(qū)域總和大于物理內(nèi)存限制,拋出OutOfMemoryError異常。

2.3 Hotspot虛擬機

2.3.1 對象的創(chuàng)建

new 對象-------》常量池定位類引用---------》檢查類引用和引用的類是否被加載、解析和初始化---------》沒有,就先執(zhí)行類加載。----》創(chuàng)建對象分配內(nèi)存

類加載通過后----》虛擬機從java堆中分配內(nèi)存---》內(nèi)存規(guī)整(已分配的內(nèi)存和未分配的內(nèi)存區(qū)域由一個指針劃分開):分配內(nèi)存時把該指針移動一個對象大小的距離,成為指針碰撞;內(nèi)存不規(guī)整時:從一個大的空閑列表區(qū)域分配內(nèi)存。

分配內(nèi)存時存在并發(fā)問題----》兩種解決方法:1 對分配內(nèi)存空間的動作進行同步處理(實際上虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性);2 把內(nèi)存分配的動作按照線程劃分在不同的空間之中進行,即每個線程在java堆中預先分配一小塊內(nèi)存,成為本地線程分配緩沖。

new指令執(zhí)行時,對象的所有字段仍為零,需要等init方法執(zhí)行后,字段才會賦予新值。

2.3.2 對象的內(nèi)存區(qū)域

在HotSpot虛擬機中,對象在內(nèi)存中存儲的布局可以分為3塊區(qū)域:對象頭(Header)、實例數(shù)據(jù)(Instance Data)和對齊填充(Padding)。

HotSpot虛擬機的對象頭包括兩部分信息,第一部分用于存儲對象自身的運行時數(shù)據(jù),如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標志、線程持有的鎖、偏向線程ID、偏向時間戳等。另一部分是類型指針,即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。

2.3.3 對象的訪問定位

對象的訪問定位也取決于具體的虛擬機實現(xiàn)。當我們在堆上創(chuàng)建一個對象實例后,就要通過虛擬機棧中的reference類型數(shù)據(jù)來操作堆上的對象。現(xiàn)在主流的訪問方式有兩種(HotSpot虛擬機采用的是第二種):

使用句柄訪問對象。即reference中存儲的是對象句柄的地址,而句柄中包含了對象實例數(shù)據(jù)與類型數(shù)據(jù)的具體地址信息,相當于二級指針。

直接指針訪問對象。即reference中存儲的就是對象地址,相當于一級指針。

兩種方式有各自的優(yōu)缺點。當垃圾回收移動對象時,對于方式一而言,reference中存儲的地址是穩(wěn)定的地址,不需要修改,僅需要修改對象句柄的地址;而對于方式二,則需要修改reference中存儲的地址。從訪問效率上看,方式二優(yōu)于方式一,因為方式二只進行了一次指針定位,節(jié)省了時間開銷,而這也是HotSpot采用的實現(xiàn)方式

2.4 OutOfMemoryError異常

2.4.1 java堆溢出

2.4.2 虛擬機棧溢出和本地方法棧溢出

2.4.3 方法區(qū)和運行時常量池區(qū)

2.4.4 本機直接內(nèi)存溢出

第三章 垃圾收集器與內(nèi)存分配策略

3.1 概述

程序計數(shù)器、虛擬機棧、本地方法棧等3個區(qū)域隨線程而生,隨線程而滅,因為方法結(jié)束或線程結(jié)束時,內(nèi)存自然就跟著回收了。而Java堆和方法區(qū)則不一樣,一個接口中的多個實現(xiàn)類需要的內(nèi)存可能不一樣,一個方法中的多個分支需要的內(nèi)存也可能不一樣,我們只有在程序出于運行期間才能知道會創(chuàng)建哪些對象,這部分內(nèi)存的分配和回收都是動態(tài)的,垃圾收集器所關(guān)注的是這部分內(nèi)存。

3.2 判斷對象是否已死

3.2.1 引用計數(shù)算法

算法:給對象添加一個引用計數(shù)器,每當有一個地方引用它時,計數(shù)器就加1,當失效時,計數(shù)器就減1,任何時刻計數(shù)器為0的對象就是不可能再被用的。缺點:難以解決對象之間循環(huán)相互引用的問題。

3.2.2 可達性分析算法

在Java中,是通過可達性分析(Reachability Analysis)來判定對象是否存活的。該算法的基本思路就是通過一些被稱為(GC Roots)的對象作為起點,從這些節(jié)點開始向下搜索,搜索走過的路徑被稱為(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時(即從GC Roots節(jié)點到該節(jié)點不可達),則證明該對象是不可用的。

java虛擬機-jvm內(nèi)存回收算法篇

 

在Java中,可作為GC Root的對象包括以下幾種:

虛擬機棧(棧幀中的本地變量表)中引用的對象

方法區(qū)中類靜態(tài)屬性引用的對象

方法區(qū)中常量引用的對象

本地方法棧中JNI(即一般說的Native方法)引用的對象

3.2.3 引用

在JDK1.2后,java對引用進行了擴充,將引用分為強引用、軟引用、弱引用和虛引用。這四種引用強度依次減弱。

強引用:是指創(chuàng)建一個對象并把這個對象賦給一個引用變量,比如:Object object =new Object(),強引用有引用變量指向時永遠不會被垃圾回收,JVM寧愿拋出OutOfMemory錯誤也不會回收這種對象。

軟引用:用來描述一些還有用但并非必須的對象。對于軟引用關(guān)聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會把這些對象列進回收范圍之中進行第二次回收。如果這次回收還沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常。在jdk1.2后,提供了SoftReference類來實現(xiàn)軟引用。

弱引用:它是用來描述非必須對象的,但是他的強度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前。當垃圾收集器工作時,無論當前內(nèi)存是否足夠,都會回收掉只被弱引用關(guān)聯(lián)的對象。在JDK1.2后,提供了WeakReference類來實現(xiàn)弱引用。

虛引用(也稱幽靈引用或者幻影引用):它是最弱的一種引用關(guān)系。一個對象是否有虛引用的存在,完全不會對其生存時間構(gòu)成影響,也無法通過虛引用來取得一個對象實例。為一個對象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是在這個對象被垃圾回收器回收時收到一個系統(tǒng)通知。在JDK 1.2之后,提供了PhantomReference類來實現(xiàn)虛引用。

3.2.4 生存還是死亡

即使在可達性分析算法中不可達的對象,也并非是非死不可的,這時候它們暫時處于緩刑階段,要真正宣告一個對象的死亡,至少要經(jīng)過兩次標記階段:

如果對象在進行可達性分析后發(fā)現(xiàn)沒有與GC Roots相連的引用鏈,那么它將會被第一次標記并且進行一次篩選,篩選的條件是此對象是否有必要執(zhí)行finalize()方法。

當對象沒有覆蓋finalize()方法,或者finalize()方法已經(jīng)被虛擬機調(diào)用過,虛擬機將這兩種情況都視為沒有必要執(zhí)行。

如果這個對想法被判定為有必要執(zhí)行finalize()方法,那么這個對象將會放置在一個叫作F-Queue的隊列中,并且稍后由一個由虛擬機自動建立的、低優(yōu)先級的Finalizer線程去執(zhí)行它。這里的‘執(zhí)行’是指虛擬機會觸發(fā)這個方法,但并不承諾會等待它運行結(jié)束,這樣做的原因是,如果一個對象在finalize()方法中執(zhí)行緩慢,或者發(fā)生了死循環(huán),將可能導致F-Queue隊列中其他對象永久處于等待,甚至導致整個內(nèi)存回收系統(tǒng)崩潰。finalize()方法是對象逃脫死亡的最后一次機會,稍后GC將對F-Queue中的對象進行第二次小規(guī)模標記,如果對象要在finalize()中成功拯救自己----------只要重新與引用鏈上的任何一個對象建立關(guān)聯(lián)即可,譬如把自己(this關(guān)鍵字)賦值給某個類變量或者對象的成員變量,那在第二次標記時它將被移除出‘即將回收’的集合;如果對象這時候還沒有逃脫,那么它就真的被回收了。例如:

public class FinalizeEscape {
	public static FinalizeEscape SAVE_HOOK = null;
	
	public void isAlive(){
		System.out.println(" i am alive");
	}
 
	@Override
	protected void finalize() throws Throwable {
		// TODO Auto-generated method stub
		super.finalize();
		System.out.println("finallize method exec");
		FinalizeEscape.SAVE_HOOK = this;
	}
	public static void main(String[] args) throws InterruptedException {
		SAVE_HOOK = new FinalizeEscape();
		
		SAVE_HOOK = null;
		System.gc();
		
		Thread.sleep(5000);
		
		if(SAVE_HOOK != null){
			SAVE_HOOK.isAlive();
		}else{
			System.out.println(" i am dead");
		}
		
		SAVE_HOOK = null;
		System.gc();
		Thread.sleep(5000);
		
		if(SAVE_HOOK != null){
			SAVE_HOOK.isAlive();
		}else{
			System.out.println(" i am dead");
		}
	}
	
	
}

輸出:

3.2.5 回收方法區(qū)

方法區(qū)是所有線程共享的一片內(nèi)存區(qū)域。它存儲的是已被JVM加載的類信息,常量,靜態(tài)變量,編譯器編譯后的代碼等數(shù)據(jù)。在JDK1.8以前的HotSpot虛擬機中,方法區(qū)也被稱為永久代,1.8后被元空間取代。方法區(qū)稱為永久代并不意味這進入方法區(qū)就永久存在,方法區(qū)也會發(fā)生內(nèi)存回收,此區(qū)域的內(nèi)存回收主要是針對常量池的回收以及對類型的卸載。我們已經(jīng)知道,GC在進行垃圾回收之前,先要進行回收對象的判斷(回收對象判斷算法:引用計數(shù)法,可達性分析算法),然后再進行垃圾回收。

很多人認為方法區(qū)(或者Hotspot中的永久代)是沒有垃圾收集的,jvm規(guī)范中確實說過可以不要求在方法區(qū)實現(xiàn)垃圾收集,而且在方法區(qū)進行垃圾收集的性價比比較低:在堆中,新生代的常規(guī)應用進行一次垃圾收集一般可以回收70%~95%的空間,而永久代的垃圾收集效率遠低于此。

永久代的垃圾收集主要回收兩部分內(nèi)容:廢棄常量和無用的類。回收廢棄常量和回收java堆中的對象非常相似。以常量池中字面量的回收為例,假如一個字符串“abc"已經(jīng)進入了常量池中,但是當前系統(tǒng)沒有任何一個String對象是叫做“abc”的,換句話說,就是沒有任何String對象引用常量池中的“abc”常量,也沒有其他地方引用了這個字面量,如果這是發(fā)生內(nèi)存回收,而且必要的話,這個“abc"常量就會被系統(tǒng)清理出常量池。常量池中的其他類(接口)、方法、字段的符號引用也與此類似。

判斷一個常量是否是廢棄常量比較簡單,而要判定一個類是否是無用的類的條件相對苛刻很多。類需要同時滿足下面三個條件才能算是無用的類:

該類的所有的實例都已經(jīng)被回收,也就是java堆中不存在該類的任何實例;

加載該類的ClassLoader被回收;

該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

3.3 垃圾收集算法

JVM中的堆,一般分為三大部分:新生代、老年代、永久代。

當前商業(yè)虛擬機的垃圾收集都采用分代收集算法,這種算法是根據(jù)各個年代的特點采用最適當?shù)氖占惴āR话闶前裫ava堆分為新生代和老年代,這樣就可以根據(jù)各個年代的特點采用最適當?shù)氖占惴āT谛律校看卫占瘯r都發(fā)現(xiàn)有大量對象死去,只有少量存活,那就選用復制算法,只需要付出少量的復制成本就可以完成收集。而老年代中因為對象存活率高,沒有額外空間對它進行分配擔保,就必須使用標記-清除或者標記-整理算法進行回收。

新生代主要是用來存放新生的對象。一般占據(jù)堆的1/3空間。由于頻繁創(chuàng)建對象,所以新生代會頻繁觸發(fā)MinorGC進行垃圾回收。新生代又分為 Eden區(qū)、ServivorFrom、ServivorTo三個區(qū)。當JVM無法為新建對象分配內(nèi)存空間的時候(Eden滿了),Minor GC被觸發(fā)。因此新生代空間占用率越高,Minor GC越頻繁。

MinorGC的過程:采用復制算法。

首先,把Eden和ServivorFrom區(qū)域中存活的對象復制到ServicorTo區(qū)域(如果有對象的年齡以及達到了老年的標準,一般是15,則賦值到老年代區(qū))

同時把這些對象的年齡+1(如果ServicorTo不夠位置了就放到老年區(qū))

然后,清空Eden和ServicorFrom中的對象;最后,ServicorTo和ServicorFrom互換,原ServicorTo成為下一次GC時的ServicorFrom區(qū)。

java虛擬機-jvm內(nèi)存回收算法篇

 

老年代的對象比較穩(wěn)定,所以MajorGC不會頻繁執(zhí)行。

在進行MajorGC前一般都先進行了一次MinorGC,使得有新生代的對象晉身入老年代,導致空間不夠用時才觸發(fā)。當無法找到足夠大的連續(xù)空間分配給新創(chuàng)建的較大對象時也會提前觸發(fā)一次MajorGC進行垃圾回收騰出空間。

MajorGC采用標記—清除算法:

首先掃描一次所有老年代,標記出存活的對象

然后回收沒有標記的對象。

MajorGC的耗時比較長,因為要掃描再回收。MajorGC會產(chǎn)生內(nèi)存碎片,為了減少內(nèi)存損耗,我們一般需要進行合并或者標記出來方便下次直接分配。

當老年代也滿了裝不下的時候,就會拋出OOM(Out of Memory)異常。

永久代指內(nèi)存的永久保存區(qū)域,主要存放Class和Meta(元數(shù)據(jù))的信息。

Class在被加載的時候被放入永久區(qū)域。它和和存放實例的區(qū)域不同,GC不會在主程序運行期對永久區(qū)域進行清理。所以這也導致了永久代的區(qū)域會隨著加載的Class的增多而脹滿,最終拋出OOM異常。

在Java8中,永久代已經(jīng)被移除,被一個稱為“元數(shù)據(jù)區(qū)”(元空間)的區(qū)域所取代。

元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機中,而是使用本地內(nèi)存。因此,默認情況下,元空間的大小僅受本地內(nèi)存限制。類的元數(shù)據(jù)放入 native memory, 字符串池和類的靜態(tài)變量放入java堆中. 這樣可以加載多少類的元數(shù)據(jù)就不再由MaxPermSize控制, 而由系統(tǒng)的實際可用空間來控制。

Major GC和Full GC區(qū)別

Full GC:收集young gen、old gen、perm gen

Major GC:有時又叫old gc,只收集old gen

3.3.1 標記-清除算法(老年代回收算法)

標記-清除算法(最基礎(chǔ)的算法)分為標記和清除兩個階段:首先標記處所有需要回收的對象,在標記完成之后統(tǒng)一回收所有被標記的對象,它的標記過程其實在前一節(jié)講述對象標記判定時已經(jīng)介紹過了。

缺點:一個是效率問題,標記和清除兩個過程的效率都不高;另一個是空間問題,標記清除之后會產(chǎn)生大量的不連續(xù)的內(nèi)存碎片,空間碎片較多時會導致以后在程序運行過程中分配較大對象時無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作。

java虛擬機-jvm內(nèi)存回收算法篇

 

3.3.2 復制算法(新生代回收算法)

為了解決效率問題,一種稱為復制的收集算法出現(xiàn)了,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內(nèi)存用完了,就將還存活的對象復制到另一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉。這樣使得每次都是對整個半?yún)^(qū)進行內(nèi)存回收,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復雜情況,只要移動堆頂指針,按順序分配內(nèi)存即可。

優(yōu)點:實現(xiàn)簡單,運行高效。

缺點:要犧牲內(nèi)存為代價。

java虛擬機-jvm內(nèi)存回收算法篇

 

3.3.3 標記-整理算法(老年代回收算法)

由于復制算法在對象存活率較高時存在大量的復制操作,效率降低,而且浪費空間,所以老年代一般不能選擇這種算法。

根據(jù)老年代的特點,提出了標記-整理算法,標記過程與標記-清除算法一致,整理過程是先將存活的對象移到一端,然后將存活的對象邊界外的內(nèi)存清理。

java虛擬機-jvm內(nèi)存回收算法篇

 

3.4 HotSpot的算法實現(xiàn)

3.5 垃圾收集器

3.5.1 Serial收集器

Serial收集器是最基本、發(fā)展歷史最悠久的收集器,曾經(jīng)(jdk1.3.1之前)是虛擬機新生代收集的唯一選擇。它是一個單線程的收集器,它使用一個CPU或一條收集線程完成垃圾收集工作,更重要的是在它進行垃圾收集時,必須暫停其他所有的工作線程,直到它收集結(jié)束。

直到現(xiàn)在為止,它依然是虛擬機運行在CLient模式下的默認新生代垃圾收集器。

優(yōu)點:簡單而高效,由于運行在單個CPU的環(huán)境中,沒有線程交互的開銷,可以專心做垃圾收集從而獲得最高的單線程收集效率。

缺點:有停頓時間,需要停止所有當前工作線程。

3.5.2 ParNew收集器

ParNew收集器是Serial收集器的多線程版本,除了使用多線程進行垃圾收集外,其余行為包括Serial收集器可用的所有控制參數(shù)、收集算法和回收策略等和Serial收集器完全一樣。

ParNew收集器是許多運行在Server模式下的虛擬機首選的新生代收集器,其中一個與性能無關(guān)的原因是,它能和CMS收集器配合工作。在JDK1.5時期,HotSpot推出了一款在強交互應用中幾乎可認為有劃時代意義的垃圾收集器-----------CMS收集器,這款收集器是HotSpot中第一款真正意義上的并發(fā)收集器,它第一次實現(xiàn)了讓垃圾收集線程與用戶線程同時工作。

ParNew收集器在Cpu環(huán)境中不會比Serial收集器有更好的效果,甚至由于存在線程交互的開銷,該收集器在通過超線程技術(shù)實現(xiàn)的兩個CPU的環(huán)境中都不能百分之百的保證可以超越Serial收集器。當然隨著可以使用的CPU的數(shù)量的增加,它對于GC時系統(tǒng)資源的有效利用還是很有好處的。它默認開啟的收集線程數(shù)與CPU的數(shù)量相同,在CPU非常多的環(huán)境下可以使用-XX:ParallelGCThreads參數(shù)來限制垃圾收集的線程數(shù)。

3.5.3 Parallel Scavenge收集器

Parallel Scavenge收集器是一個新生代收集器,它也是使用復制算法的收集器,又是并行的多線程收集器。

Parallel Scavenge收集器的特點是它的關(guān)注點與其它收集器不同,CMS等收集器的關(guān)注點是盡可能地縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量。所謂吞吐量就是CPU用于運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),虛擬機總共運行了100分鐘,垃圾收集1分鐘,那吞吐量就是99%。

Parallel Scavenge收集器擁有自適應的調(diào)節(jié)策略(GC Ergonomics),它會根據(jù)當前系統(tǒng)的運行狀況動態(tài)調(diào)整停頓時間和吞吐量,這也是和ParNew收集器不同之處。

3.5.4 Serial Old收集器

Serial Old收集器是Serial收集器的老年代版本,它同樣是一個單線程收集器,使用‘標記-整理’算法。這個收集器的主要意義是在于給Client模式下的虛擬機使用。如果在Server模式下,那么它還有兩大用途:一種是在JDK1.5以及之前版本中與Parallel Scavenge收集器搭配使用,另一種是作為CMS收集器的后備預案,在并發(fā)收集發(fā)生Concurrent Mode Failure時使用。

3.5.5 Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多線程和標記整理算法。這個收集器是在JDK1.6才開始提供的,在此之前,新生代的Parallel Scavenge收集器處于比較尷尬的狀態(tài),因為新生代選擇了Parallel Scavenge收集器,老年代必須選擇Serial Old(因為Parallel Scavenge收集器無法與CMS收集器搭配使用)。Serial Old由于是單線程,在服務端性能是拖累,因此即使使用了Parallel Scavenge收集器也未必能獲得吞吐量最大化的效果,而且單線程的老年代收集中無法充分利用服務器多CPU的處理能力,在老年代很大而且硬件比較高級的環(huán)境中,這種組合的吞吐量甚至還不一定有ParNew加CMS組合的能力。

3.5.6 CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。主要應用在服務端,以提供較短的響應時間。它是基于標記-清除算法實現(xiàn)。整個過程分為4個步驟:

初始標記(CMS initial mark)

并發(fā)標記(CMS concurrent mark)

重新標記(CMS remark)

并發(fā)清除(CMS concurrent sweep)

其中初始標記和重新標記依然會有停頓時間(Stop the world)。初始標記僅僅只是標記一下GC Roots能直接關(guān)聯(lián)到的對象,速度很快,并發(fā)標記就是進行GC Roots Tracing的過程,而重新標記則是為了修正并發(fā)標記期間因用戶程序繼續(xù)運作而導致標記產(chǎn)生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比并發(fā)標記的時間短。

CMS優(yōu)點:并發(fā)收集,低停頓。

缺點:

CMS收集器對CPU資源非常敏感。在CMS中并發(fā)階段,它依賴CPU資源,若CPU資源較少時,CMS會消耗掉比較多的一部分CPU資源,使得程序的執(zhí)行速度變慢。

CMS收集器無法處理浮動垃圾,可能出現(xiàn)“Concurrent Mode Failure”失敗而導致另一次Full GC的產(chǎn)生。由于CMS并發(fā)清理階段用戶程序還在運行,就會產(chǎn)生新的垃圾,這一部分垃圾出現(xiàn)在標記過程后,只好留到下一次GC時清理,這一部分垃圾成為

"浮動垃圾"。

CMS收集器使用的時標記-清除算法,所以會產(chǎn)生內(nèi)存碎片,導致大對象申請內(nèi)存時,無法找到足夠大的連續(xù)空間來分配對象,不得不提前觸發(fā)Full GC。

3.5.7 G1收集器

為解決CMS算法產(chǎn)生空間碎片和其它一系列的問題缺陷,HotSpot提供了另外一種垃圾回收策略,G1(Garbage First)算法,通過參數(shù)-XX:+UseG1GC來啟用,該算法在JDK 7u4版本被正式推出,官網(wǎng)對此描述如下:

G1垃圾收集算法主要應用在多CPU大內(nèi)存的服務中,在滿足高吞吐量的同時,竟可能的滿足垃圾回收時的暫停時間,該設(shè)計主要針對如下應用場景:

垃圾收集線程和應用線程并發(fā)執(zhí)行,和CMS一樣

空閑內(nèi)存壓縮時避免冗長的暫停時間

應用需要更多可預測的GC暫停時間

不希望犧牲太多的吞吐性能

不需要很大的Java堆 (翻譯的有點虛,多大才算大?)

第四章 虛擬機性能監(jiān)控與故障處理工具

4.1 概述

了解了虛擬機內(nèi)存分配與回收技術(shù)的介紹,再根據(jù)從實踐的角度去了解虛擬機內(nèi)存管理的世界。

給一個系統(tǒng)定位問題的時候,知識、經(jīng)驗是關(guān)鍵基礎(chǔ),數(shù)據(jù)是依據(jù),工具是運用處理數(shù)據(jù)的手段。這里說的數(shù)據(jù)包括:運行日志、異常堆棧、GC日志、線程快照(threaddump/javacore文件)、堆轉(zhuǎn)儲快照(heapdump/hprof文件)等。經(jīng)常使用適當?shù)奶摂M機監(jiān)控和分析的工具可以加快我們分析數(shù)據(jù)、定位解決問題的速度,但在學習工具前,也應當意識到工具永遠都是知識技能的一層包裝,沒有什么工具是秘密武器。

4.2 JDK的命令行工具

Sun jdk監(jiān)控和故障處理工具

java虛擬機-jvm內(nèi)存回收算法篇

 

4.2.1 jps:虛擬機進程狀況工具

java虛擬機-jvm內(nèi)存回收算法篇

 

jps [option] [hostid]

-q 只輸出LVMID,省略主類的名稱

-m 輸出虛擬機進程啟動時傳遞給主類main()函數(shù)的參數(shù)

-l 輸出主類的全名,如果進程執(zhí)行的是Jar包,輸出Jar路徑

-v 輸出虛擬機進程啟動JVM參數(shù)

4.2.2 jstat:虛擬機統(tǒng)計信息監(jiān)視工具

jstat [option vmid [interval[s|ms] [count] ]

-class 監(jiān)視類裝載、卸載數(shù)量、總空間以及類裝載所耗費的時間

-gc 監(jiān)視java堆狀況,包括Eden區(qū)、兩個suivivor區(qū)、老年代、永久代等的容量、已用空間、GC時間合計等信息

-gccapacity 監(jiān)視內(nèi)容與gc基本相同,但輸出主要關(guān)注Java堆各個區(qū)域使用到的最大、最小空間

-gcutil 監(jiān)視內(nèi)容與gc基本相同,但輸出主要關(guān)注已使用空間占總空間的百分比

-gccause 與-gcutil功能一樣,但是會額外輸出導致上一次GC產(chǎn)生的原因

-gcnew 監(jiān)視新生代GC狀況

4.2.3 jinfo:Java配置信息工具

jinfo [option] pid

4.2.4 jmap:Java內(nèi)存映像工具

jmap [option] vmid

————————————————

版權(quán)聲明:本文為CSDN博主「24koby」的原創(chuàng)文章,遵循CC 4.0 by-sa版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/qq_31583183/article/details/95059493

分享到:
標簽:虛擬機 java
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定