Java內存區域
- 程序計數器:當前線程所執行字節碼的行號指示器,字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令
- 虛擬機棧:指咱們日常所說的堆棧中的棧(或者說是虛擬機棧中的局部變量部分),線程私有的,生命週期與線程相同。用來描述Java方法執行的內存模型,用於存儲局部變量表、操做棧、動態連接、方法出口等信息
- 本地方法棧:跟虛擬機棧相似,不過本地方法棧是爲虛擬機使用到的Native方法服務
- 堆:被全部線程共享的一塊內存區域,用來存放對象實例以及對象類型數據的地址信息,還有字符串常量池(JDK7.0後放入堆中,JDK6.0及以前的版本在方法區中),若是堆中沒有內存完成實例分配,而且堆也沒法再擴展時,將會拋出OutOfMemoryError異常
- 方法區:跟堆同樣,是各個線程共享的內存區域,用來存儲類信息(對象類型、父類、實現的接口、方法等)、靜態變量,其中方法區還包含一個運行時常量池,用來存儲編譯期生成的各類字面量和符號引用
垃圾收集器及內存分配策略
對象存活判斷
垃圾回收主要是回收堆內存。在垃圾回收期(GC)回收以前,須要肯定哪些對象能夠回收,有如下幾種方法:java
- 引用計數算法
原理:給對象添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任什麼時候刻計數器都爲0的對象就是不可能再被使用的。這種算法效率高。不過很難解決對象之間的相互循環引用的問題。
- 根搜索算法(默認)
原理:經過一系列的名爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的。做爲GC Roots的對象包括如下幾種:
- 虛擬機棧中的引用的對象
- 方法區中的類靜態屬性引用的對象
- 方法區中的常量引用的對象
- 本地方法棧中JNI的引用的對象
引用
- 強引用,相似"Object obj = new Object()"這種,只要強引用存在,則GC永遠不會回收被引用的對象
- 軟引用,指還有用,可是並不是必須的對象,內存溢出以前進行回收,實現軟引用能夠經過SoftReference類,軟引用主要用戶實現相似緩存的功能,在內存足夠的狀況下直接經過軟引用取值,無需從繁忙的真實來源查詢數據,提高速度;當內存不足時,自動刪除這部分緩存數據,從真正的來源查詢這些數據。
- 弱引用,跟軟引用同樣,不過強度比軟引用弱一些,第二次垃圾回收時回收,實現弱引用能夠經過WeakReference類,弱引用主要用於監控對象是否已經被垃圾回收器標記爲即將回收的垃圾,能夠經過弱引用的isEnQueued方法返回對象是否被垃圾回收器標記。
- 虛引用,是最弱的一種引用關係,垃圾回收時回收,沒法經過引用取到對象值。主要用於檢測對象是否已經從內存中刪除。
垃圾收集算法
- 標記-清除算法:首先標記出全部須要回收的對象,在標記完成後統一回收全部被標記的對象,不過該算法有如下缺點:
- 效率低
- 空間問題,該算法會產生大量不連續的內存碎片,這樣致使程序在之後的運行中若是須要分配較大對象時沒法找到足夠的連續內存而觸發另外一次垃圾收集動做
- 複製算法:將可用內存按容量劃分大小相等的兩塊,每次只使用其中的一塊。當一塊內存用完了,就將還存活的對象複製到另一塊上面,而後再把已使用過的內存空間一次清理掉。這種算法實現簡單,效率高,不過會將可以使用的內存減小一半。若是對象存活率高就要執行較多的複製操做,將致使效率變低。目前在複製算法中,一般是將內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次只使用Eden和一塊Survivor。當回收時,將Eden和Survivor中還存活的對象一次性拷貝到另一塊Survivor中,最後清理使用的Eden和Survivor。而且以老年代做爲空間分配擔保,即Survivor沒法容納的對象會直接進入老年代。目前新生代主要採用這個算法。
- 標記-整理算法:將全部存活的對象都向一端移動,而後直接清理掉邊界之外的內存。
- 分代收集算法:根據對象的存活週期的不一樣將內存劃分爲幾塊,通常是分爲新生代和老年代。而後根據各個年代的特色採用最適當的收集算法。新生代一般採用複製算法,由於對象生存時間都不長。老年代通常採用"標記-清理"或者"標記-整理"算法回收,由於老年代中對象存活率高,沒有額外空間對它進行分配擔保。
垃圾收集器
- Serial收集器:這是一個單線程的收集器,該收集器在進行垃圾收集時,必須暫停其它全部的工做線程,不過該收集器簡單而高效。它通常運行在Client模式下的虛擬機。新生代收集器。
- ParNew收集器:這是一個多線程版本的Serial收集器。新生代收集器。
- Parallel Scavenge收集器:新生代收集器,使用複製算法,並行多線程。它主要是控制吞吐量=(運行用戶代碼時間)/(運行用戶代碼時間+垃圾收集時間)。有兩個參數能夠用來精確控制吞吐量。
- -XX:MaxGCPauseMillis:設置最大垃圾收集停頓時間
- -XX:GCTimeRatio:設置吞吐量大小
- -XX:+UseAdaptiveSizePolicy:開關參數,打開之後就由虛擬機自動調節策略。這也是跟ParNew收集器的一個重要區別
- Serial Old收集器:這是Serial收集器的老年代版本,使用"標記-整理"算法,該收集器有兩大用途,一是與Parallel Scavenge收集器搭配使用。二是做爲CMS收集器的後備預案,在併發收集發生Concurrent Mode Failure的時候使用。
- Parallel Old收集器:是Parallel Scavenge收集器的老年代版本。使用"標記-整理"算法。
- CMS收集器:以獲取最短回收停頓時間爲目標的收集器。重視服務的響應速度。主要用於互聯網或B/S系統的服務端上。基於"標記-清除"算法。老年代收集器。使用CMS收集器時,不能像其它收集器那樣等到老年代幾乎被填滿了再進行收集,須要預留一部分空間提供併發收集時的程勳運行使用。主要優勢就是併發收集、低停頓。它有如下缺點:
- 對CPU資源很是敏感
- 沒法處理浮動垃圾。即在垃圾收集階段用戶線程繼續進行,這個過程會有新的垃圾產生,可是CMS收集器不會對這部分垃圾(浮動垃圾)進行標記。若是預留的內存空間不足,就會致使Concurrent Mode Failure。這時會臨時啓動Serial Old收集器從新進行老年代的垃圾收集,致使停頓時間過長。
- 因爲採用的算法,會致使收集結束時產生大量空間碎片。
- G1收集器:基於"標記-整理"算法實現。能夠很是精確的控制停頓,而且在不犧牲吞吐量的前提下完成低停頓的內存回收。由於該收集器會將整個java堆劃分爲多個大小固定的獨立區域,而且跟蹤這些區域的垃圾堆積程度,每次根據容許的收集時間,優先回收垃圾最多的區域。 垃圾收集相關的經常使用參數參見下圖:
內存分配
- 對象首先在新生代Eden區分配,當Eden沒有足夠的空間進行分配時,虛擬機將發起一次MinorGC
- 大對象直接進入老年代,經過-XX:PretenureSizeThreshold參數區分,大於這個值的表示大對象,該參數只對Serial和ParNew有效
- 長期存活的對象將進入老年代,識別對象長期存活經過參數-XX:MaxTenuringThreshold設定,對於每一個對象而言,虛擬機都會定義一個年齡計數器,若是對象在通過第一次MinorGC後仍然存活,而且可以被Survivor容納的話,就將被移動到Survivor中,而且年齡增長一歲。每通過一次MinorGC,年齡就增長一歲,超過設定值或者默認值(15),就會進入老年代中。
名詞解釋
- MinorGC:指發生在新生代的垃圾收集動做,頻繁,速度快
- MajorGC/Full GC:指發生在老年代的GC,速度慢
- 大對象:須要大量連續內存空間的Java對象,好比很長的字符串及數組,應當避免短命的大對象