堆內存劃分爲 新生代(Eden空間、Survivor空間)和 老年代(Tenured/Old 空間)。
安全
大可能是狀況下,對象在新生代Eden區中分配。當Eden區中沒有足夠空間進行分配時,虛擬機將發起一次Minor GC
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 打印gc日誌
-Xms20M -Xmx20M 指定堆內存
-Xmn10M 指定新生代內存
-XX:SurvivorRatio=8 指定Eden區與一個Survivor區的空間比例是8:1。
併發
private static final int _1MB = 1024 * 1024; public static void main(String[] args) { byte[] allocation1 = new byte[2 * _1MB]; byte[] allocation2 = new byte[2 * _1MB]; byte[] allocation3 = new byte[2 * _1MB]; byte[] allocation4 = new byte[4 * _1MB]; }
[GC (Allocation Failure) [PSYoungGen: 6420K->1010K(9216K)] 6420K->3594K(19456K), 0.0157046 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] Heap PSYoungGen total 9216K, used 5427K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 53% used [0x00000000ff600000,0x00000000ffa50630,0x00000000ffe00000) from space 1024K, 98% used [0x00000000ffe00000,0x00000000ffefc888,0x00000000fff00000) to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) ParOldGen total 10240K, used 6680K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 65% used [0x00000000fec00000,0x00000000ff286298,0x00000000ff600000) Metaspace used 3305K, capacity 4500K, committed 4864K, reserved 1056768K class space used 357K, capacity 388K, committed 512K, reserved 1048576KPSYoungGen 使用了Parallel收集器,關於JVM垃圾收集器漫談,能夠參考《JVM 之(5)垃圾收集器》。
分配allocation4對象的語句會發生一次MinorGC,給allocation4分配內存時,發現Eden已經被佔用了6MB,剩餘空間已不足分配all4所需的4MB內存,所以發生一次MinorGC。高併發
GC期間,虛擬機又發現已有的3個2MB大小的對象所有沒法放入Survivor空間(Survivor空間只有1MB大小),因此只好經過分配擔保機制提早分配到老年代去。性能
GC結束後,4MBallocation4對象順利分配在Eden中,Survivor空閒,老年代被佔用6MB。
this
對於體積較大的對象,直接進入老年代區域而不是分配到新生代。
JVM參數-XX:PretenureSizeThreshold的意思就是將體積大於這個設置值的對象直接在老年代分配。 這樣作是爲了不在Eden區及兩個Survivor區之間發生大量的內存複製。
PretenureSizeThreshold參數只對 Serial 和 ParNew兩款收集器有效。
spa
private static final int _1MB = 1024 * 1024; public static void main(String[] args) { byte[] allocation = new byte[7 * _1MB]; }
PSYoungGen total 9216K, used 4537K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 55% used [0x00000000ff600000,0x00000000ffa6e478,0x00000000ffe00000) from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000) ParOldGen total 10240K, used 7168K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 70% used [0x00000000fec00000,0x00000000ff300010,0x00000000ff600000) Metaspace used 3326K, capacity 4500K, committed 4864K, reserved 1056768K class space used 361K, capacity 388K, committed 512K, reserved 1048576K
既然虛擬機採用了分代收集的思想來管理內存,那麼內存回收時就必須能識別哪些對象應放在新生代,哪些對象應放在老年代中。
爲了作到這點,虛擬機給每一個對象定義了一個對象年齡(Age)計數器。 若是對象在Eden出生並通過第一次Minor GC後仍然存活,而且能被Survivor容納的話,將被移動到Survivor空間中,而且對象年齡設爲1。
對象在Survivor區中每「熬過」一次Minor GC,年齡就增長1歲,當它的年齡增長到必定程度(默認爲15歲),就將會被晉升到老年代中。
對象晉升老年代的年齡閾值,能夠經過參數-XX:MaxTenuringThreshold設置。
.net
爲了能更好地適應不一樣程序的內存情況,虛擬機並非永遠地要求對象的年齡必須達到了MaxTenuringThreshold才能晉升老年代。
若是在Survivor空間中相同年齡全部對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就能夠直接進入老年代,無須等到MaxTenuringThreshold中要求的年齡。
線程
若是小於,或者-XX:HandlePromotionFailure設置不容許冒險,那這時也要改成進行一次Full GC。日誌
觸發Full GC執行的狀況:
1. System.gc();
2 . 老年代空間不足
3. Permanet Generation空間滿
4. CMS GC時出現promotion failed和concurrent mode failure(《JVM 之(5)垃圾收集器》中提到)
5. 統計獲得的Minor GC晉升到老年代的平均大小大於舊生代的剩餘空間
對象
public class StackAllocation { public StackAllocation obj; /** * 方法返回StackAllocation對象,發生逃逸 * @return */ public StackAllocation getInstance(){ return obj == null ? new StackAllocation() : obj; } /** * 爲成員變量賦值,發生逃逸 */ public void setObj(){ this.obj = new StackAllocation(); } /** * 對象做用域在方法體內,沒有發生逃逸 */ public void useStackAllocation(){ StackAllocation stackAllocation = new StackAllocation(); } /** * 引用成員變量,發生逃逸 */ public void useStackAllocationObj(){ StackAllocation stackAllocation = getInstance(); } }
5. 逃逸分析/棧上分配的優劣分析
優點表如今如下兩個方面: