認識java虛擬機器(3)
記憶體分配策略:
優先分配到eden
大物件直接分配到老年代
長期存活的物件分配到老年代
空間分配擔保
動態物件年齡判斷
1.優先分配到eden
-verbose:gc -XX:+PrintGCDetails 預設是Parallel Scavenge 收集器,標誌為:PSYoungGen
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC 指定Serial收集器 ,標誌為:def new generation
public class Main { public static void main(String[] args) { byte [] m=new byte[4*1024*1024]; } }
程式碼中為byte陣列分配了4M的空間,執行結果:大致分析
堆
新生代總計記憶體33M,使用了6M,eden區域 記憶體總計30M,21%的空間被使用,tenured generation養老區記憶體總計75M
更改程式碼如下:
public class Main {
public static void main(String[] args) {
byte [] m=new byte[40*1024*1024];
}
}
執行發現:大物件被直接分配到了tenured generation,eden區域的使用記憶體減少
指定jvm引數:
-verbose:gc(-verbose:gc 是 -XX:+PrintGC的別名,列印gc執行資訊)
-XX:+PrintGCDetails(詳細列印jvm執行日誌)
-XX:+UseSerialGC(指定serial GC垃圾回收器)
-Xms20M(設定JVM最大可用記憶體為20M)
-Xmx20M(用來設定程式初始化的時候記憶體棧的大小,增加這個值的話你的程式的啟動效能會得到提高
此值可以設定與-Xmx相同,以避免每次垃圾回收完成後JVM重新分配記憶體)
-Xmn10M(指定新生代記憶體10M)
-XX:SurvivorRatio=8 (指定新生代的eden為8M)
-XX:PretenureSizeThreshold=6M(指定6M以上的就判定為大物件,大物件直接分配到tenured區)
public class Main {
public static void main(String[] args) {
//m1,m2,m3被分配到了tenured區域 8.192M*58%=4M
byte [] m1=new byte[2*1024*1024];
byte [] m2=new byte[2*1024*1024];
byte [] m3=new byte[2*1024*1024];
//m4被分配到了eden區,10M*60%=6M
byte [] m4=new byte[4*1024*1024];
//呼叫gc垃圾回收,回收from space和to space 垃圾區域所佔的記憶體
System.gc();
}
}
[GC (Allocation Failure) [DefNew: 7129K->530K(9216K),] 7129K->6674K(19456K),
<p>呼叫allocation gc,回收了eden的7M空間,6M的三個物件仍存活,故未被清理掉</p>
[Full GC (System.gc()) [Tenured: 6144K->6144K(10240K)] 10930K->10768K(19456K), [Metaspace: 2654K->2654K(1056768K)]
<h1>呼叫full gc,清理掉from space的1M垃圾空間</h1>
Heap
def new generation total 9216K, used 4788K
eden space 8192K, 58% used
from space 1024K, 0% used
to space 1024K, 0% used
tenured generation total 10240K, used 6144K
the space 10240K, 60% used
Metaspace used 2660K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 286K, capacity 386K, committed 512K, reserved 1048576K
長期存活的物件進入老年代
-XX:MaxTenuringThreshold 15
Age 1+1+1
空間分配擔保:
-XX:+HandlePromotionFailure(先衡量有無足夠的能力【空間】滿足你的記憶體分配的需求,+是開啟,-是禁用,老年代的空間並不能容納下所有的新生代的物件的空間)
逃逸分析與棧上分配
棧上分配:方法執行需要建立一個棧幀,方法執行,棧幀進棧出棧,棧區域是根據方法的執行來進行分配與釋放,不需要垃圾回收器來處理,效能很高
為逃逸的物件的可在棧上進行分配,分析出物件的作用域(方法體內部,方法內有效,它為逃逸,故可分配在棧上)
逃逸分析的情況:
public class StackAllocation {
public StackAllocation obj;
/* 方法返回StackAllocation物件,放生逃逸*/
public StackAllocation getInstance() {
return obj==null?new StackAllocation():obj;
}
/* 為成員屬性賦值,放生逃逸*/
public void setObj() {
this.obj=new StackAllocation();
}
/* 物件的作用域僅在當前方法中有效,沒有發生逃逸*/
public void useStackAllcation() {
/*在方法中定義物件,則物件會被直接分配到棧中,隨著方法結束,棧記憶體就會被移出,記憶體回收,物件也隨之銷燬,效能較高
能區域性建立物件,就區域性建立物件*/
StackAllocation s =new StackAllocation();
}
/* 引用成員變數的值,發生逃逸*/
public void useStackAllocation2() {
StackAllocation s=getInstance();
}
}