摘要:本人使用的是,以下涉及的默認值均以該版本為準。其中,新生代被細分為和兩個區域,這兩個區域分別被命名為和,以示區分。其中新生帶存放新生的對象或者年齡不大的對象,老年代則存放老年對象。
什么是垃圾回收機制
不定時去堆內存中清理不可達對象。不可達的對象并不會馬上就會直接回收, 垃圾收集器在一個Java程序中的執行是自動的,不能強制執行,即使程序員能明確地判斷出有一塊內存已經無用了,是應該回收的,程序員也不能強制垃圾收集器回收該內存塊。程序員唯一能做的就是通過調用System.gc 方法來"建議"執行垃圾收集器,但其是否可以執行,什么時候執行卻都是不可知的。這也是垃圾收集器的最主要的缺點。當然相對于它給程序員帶來的巨大方便性而言,這個缺點是瑕不掩瑜的。
finalize方法作用Java技術使用finalize()方法在垃圾收集器將對象從內存中清除出去前,做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調用的。它是在Object類中定義的,因此所有的類都繼承了它。子類覆蓋finalize()方法以整理系統資源或者執行其他清理工作。finalize()方法是在垃圾收集器刪除對象之前對這個對象調用的。
新生代與老年代Java 中的堆是 JVM 所管理的最大的一塊內存空間,主要用于存放各種類的實例對象。
在 Java 中,堆被劃分成兩個不同的區域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被劃分為三個區域:Eden、From Survivor、To Survivor。
這樣劃分的目的是為了使 JVM 能夠更好的管理堆內存中的對象,包括內存的分配以及回收。
(本人使用的是 JDK1.6,以下涉及的 JVM 默認值均以該版本為準。)
默認的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ( 該值可以通過參數 –XX:NewRatio 來指定 ),即:新生代 ( Young ) = 1/3 的堆空間大小。老年代 ( Old ) = 2/3 的堆空間大小。其中,新生代 ( Young ) 被細分為 Eden 和 兩個 Survivor 區域,這兩個 Survivor 區域分別被命名為 from 和 to,以示區分。
默認的,Edem : from : to = 8 : 1 : 1 ( 可以通過參數 –XX:SurvivorRatio 來設定 ),即: Eden = 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小。
根據垃圾回收機制的不同,Java堆有可能擁有不同的結構,最為常見的就是將整個Java堆分為
新生代和老年代。其中新生帶存放新生的對象或者年齡不大的對象,老年代則存放老年對象。
新生代分為den區、s0區、s1區,s0和s1也被稱為from和to區域,他們是兩塊大小相等并且可以互相角色的空間。
絕大多數情況下,對象首先分配在eden區,在新生代回收后,如果對象還存活,則進入s0或s1區,之后每經過一次
新生代回收,如果對象存活則它的年齡就加1,對象達到一定的年齡后,則進入老年代。
-XX:+PrintGC 每次觸發GC的時候打印相關日志
-XX:+UseSerialGC 串行回收
-XX:+PrintGCDetails 更詳細的GC日志
-Xms 堆初始值
-Xmx 堆最大可用值
-Xmn 新生代堆最大可用值
-XX:SurvivorRatio 用來設置新生代中eden空間和from/to空間的比例.
-XX:NewRatio 配置新生代與老年代占比 1:2
含以-XX:SurvivorRatio=eden/from=den/to
總結:在實際工作中,我們可以直接將初始的堆大小與最大堆大小相等,這樣的好處是可以減少程序運行時垃圾回收次數,從而提高效率。
-XX:SurvivorRatio 用來設置新生代中eden空間和from/to空間的比例.
引用計數法
引用計數法就是如果一個對象沒有被任何引用指向,則可視之為垃圾。這種方法的缺點就是不能檢測到環的存在。
首先需要聲明,至少主流的Java虛擬機里面都沒有選用引用計數算法來管理內存。
什么是引用計數算法:給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值加1;當引用失效時,計數器值減1.任何時刻計數器值為0的對象就是不可能再被使用的。那為什么主流的Java虛擬機里面都沒有選用這種算法呢?其中最主要的原因是它很難解決對象之間相互循環引用的問題。
根搜索算法
根搜索算法的基本思路就是通過一系列名為”GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。
這個算法的基本思想是通過一系列稱為“GC Roots”的對象作為起始點,從這些節點向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈(即GC Roots到對象不可達)時,則證明此對象是不可用的。
那么問題又來了,如何選取GCRoots對象呢?在Java語言中,可以作為GCRoots的對象包括下面幾種:
(1). 虛擬機棧(棧幀中的局部變量區,也叫做局部變量表)中引用的對象。
(2). 方法區中的類靜態屬性引用的對象。
(3). 方法區中常量引用的對象。
(4). 本地方法棧中JNI(Native方法)引用的對象。
標記清除算法
該算法有兩個階段。
標記階段:找到所有可訪問的對象,做個標記
清除階段:遍歷堆,把未被標記的對象回收
該算法一般應用于老年代,因為老年代的對象生命周期比較長
標記清除算法的優點和缺點
1.優點
是可以解決循環引用的問題
必要時才回收(內存不足時)
2.缺點:
回收時,應用需要掛起,也就是stop the world。
標記和清除的效率不高,尤其是要掃描的對象比較多的時候
會造成內存碎片(會導致明明有內存空間,但是由于不連續,申請稍微大一些的對象無法做到)
復制算法
如果jvm使用了coping算法,一開始就會將可用內存分為兩塊,from域和to域, 每次只是使用from域,to域則空閑著。當from域內存不夠了,開始執行GC操作,這個時候,會把from域存活的對象拷貝到to域,然后直接把from域進行內存清理。
coping算法一般是使用在新生代中,因為新生代中的對象一般都是朝生夕死的,存活對象的數量并不多,這樣使用coping算法進行拷貝時效率比較高。jvm將Heap 內存劃分為新生代與老年代,又將新生代劃分為Eden(伊甸園) 與2塊Survivor Space(幸存者區) ,然后在Eden –>Survivor Space 以及From Survivor Space 與To Survivor Space 之間實行Copying 算法。 不過jvm在應用coping算法時,并不是把內存按照1:1來劃分的,這樣太浪費內存空間了。一般的jvm都是8:1。也即是說,Eden區:From區:To區域的比例是
始終有90%的空間是可以用來創建對象的,而剩下的10%用來存放回收后存活的對象。
1、當Eden區滿的時候,會觸發第一次young gc,把還活著的對象拷貝到Survivor From區;當Eden區再次觸發young gc的時候,會掃描Eden區和From區域,對兩個區域進行垃圾回收,經過這次回收后還存活的對象,則直接復制到To區域,并將Eden和From區域清空。
2、當后續Eden又發生young gc的時候,會對Eden和To區域進行垃圾回收,存活的對象復制到From區域,并將Eden和To區域清空。
3、可見部分對象會在From和To區域中復制來復制去,如此交換15次(由JVM參數MaxTenuringThreshold決定,這個參數默認是15),最終如果還是存活,就存入到老年代
注意: 萬一存活對象數量比較多,那么To域的內存可能不夠存放,這個時候會借助老年代的空間。
優點:在存活對象不多的情況下,性能高,能解決內存碎片和java垃圾回收算法之-標記清除 中導致的引用更新問題。
缺點: 會造成一部分的內存浪費。不過可以根據實際情況,將內存塊大小比例適當調整;如果存活對象的數量比較大,coping的性能會變得很差。
標記壓縮算法
標記清除算法和標記壓縮算法非常相同,但是標記壓縮算法在標記清除算法之上解決內存碎片化
任意順序 : 即不考慮原先對象的排列順序,也不考慮對象之間的引用關系,隨意移動對象;
線性順序 : 考慮對象的引用關系,例如a對象引用了b對象,則盡可能將a和b移動到一塊;
滑動順序 : 按照對象原來在堆中的順序滑動到堆的一端。
優點:解決內存碎片問題,缺點壓縮階段,由于移動了可用對象,需要去更新引用。
分代算法
這種算法,根據對象的存活周期的不同將內存劃分成幾塊,新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。可以用抓重點的思路來理解這個算法。
新生代對象朝生夕死,對象數量多,只要重點掃描這個區域,那么就可以大大提高垃圾收集的效率。另外老年代對象存儲久,無需經常掃描老年代,避免掃描導致的開銷。
新生代
在新生代,每次垃圾收集器都發現有大批對象死去,只有少量存活,采用復制算法,只需要付出少量存活對象的復制成本就可以完成收集
老年代
而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須“標記-清除-壓縮”算法進行回收。
新創建的對象被分配在新生代,如果對象經過幾次回收后仍然存活,那么就把這個對象劃分到老年代。
老年代區存放Young區Survivor滿后觸發minor GC后仍然存活的對象,當Eden區滿后會將存活的對象放入Survivor區域,如果Survivor區存不下這些對象,GC收集器就會將這些對象直接存放到Old區中,如果Survivor區中的對象足夠老,也直接存放到Old區中。如果Old區滿了,將會觸發Full GC回收整個堆內存
新生代 GC(Minor GC):指發生在新生代的垃圾收集動作,因為 Java 對象大多都具
備朝生夕滅的特性,所以 Minor GC 非常頻繁,一般回收速度也比較快。
老年代 GC(Major GC / Full GC):指發生在老年代的 GC,出現了 Major GC,經常
會伴隨至少一次的 Minor GC(但非絕對的,在 ParallelScavenge 收集器的收集策略里
就有直接進行 Major GC 的策略選擇過程) 。MajorGC 的速度一般會比 Minor GC 慢 10
倍以上。
Minor GC觸發機制:
當年輕代滿時就會觸發Minor GC,這里的年輕代滿指的是Eden代滿,Survivor滿不會引發GCFull GC觸發機制:
當年老代滿時會引發Full GC,Full GC將會同時回收年輕代、年老代,當永久代滿時也會引發Full GC,會導致Class、Method元信息的卸載其Minor
虛擬機給每個對象定義了一個對象年齡(Age)計數器。如果對象在 Eden 出生并經過第一次 Minor GC 后仍然存活,并且能被 Survivor 容納的話,將被移動到 Survivor 空間中,并將對象年齡設為 1。對象在 Survivor 區中每熬過一次 Minor GC,年齡就增加 1 歲,當它的年齡增加到一定程度(默認為 15 歲)時,就會被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過參數 -XX:MaxTenuringThreshold (閾值)來設置
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74757.html
摘要:虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。總體來說就是,我們利用調用開發了屬于我們自己的程序后,通過中的編譯程序將我們的文本文件編譯成字節碼,在上運行這些字節碼,解析這些字節碼,映射到指令集或的系統調用。 1.簡述JDK、JRE、JVM? 一、JDK JDK(Java Development Kit) 是整個JAVA的核心, 包括了Java運行環境(Java Ru...
摘要:虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。總體來說就是,我們利用調用開發了屬于我們自己的程序后,通過中的編譯程序將我們的文本文件編譯成字節碼,在上運行這些字節碼,解析這些字節碼,映射到指令集或的系統調用。 1.簡述JDK、JRE、JVM? 一、JDK JDK(Java Development Kit) 是整個JAVA的核心, 包括了Java運行環境(Java Ru...
在社會化分工、軟件行業細分專業化的趨勢下,會真的參與到底層系統實現的人肯定是越來越少(比例上說)。真的會參與到JVM實現的人肯定是少數。 但如果您對JVM是如何實現的有興趣、充滿好奇,卻苦于沒有足夠系統的知識去深入,那么可以參考RednaxelaFX整理的這個書單。 showImg(http://segmentfault.com/img/bVbGzn); 本豆列的脈絡是: 1. JV...
摘要:而字節碼運行在之上,所以不用關心字節碼是在哪個操作系統編譯的,只要符合規范,那么,這個字節碼文件就是可運行的。好處防止內存中出現多份同樣的字節碼安全性角度特別說明類加載器在成功加載某個類之后,會把得到的類的實例緩存起來。 前言 只有光頭才能變強 JVM在準備面試的時候就有看了,一直沒時間寫筆記。現在到了一家公司實習,閑的時候就寫寫,刷刷JVM博客,刷刷電子書。 學習JVM的目的也很簡單...
閱讀 1654·2019-08-30 13:04
閱讀 2205·2019-08-30 12:59
閱讀 1764·2019-08-29 18:34
閱讀 1857·2019-08-29 17:31
閱讀 1255·2019-08-29 15:42
閱讀 3530·2019-08-29 15:37
閱讀 2857·2019-08-29 13:45
閱讀 2771·2019-08-26 13:57