物件一定分配在堆中嗎?
JVM通過逃逸分析,那些逃不出方法的物件會在棧上分配。
什麼是逃逸分析?
EscapeAnalysis,逃逸分析,指的是虛擬機器在執行期
通過計算分析將原本在堆上分配的物件改成在棧中分配,這樣的好處是棧上分配的物件隨著執行緒的結束而自動銷燬,不依賴於GC,可以降低垃圾收集器執行的頻率。
如何判定為逃逸?
JVM判斷新建立的物件是否逃逸的依據有兩個:
- 物件被賦值給堆中物件的欄位和類的靜態變數
- 物件被傳進了不確定的程式碼中去執行
如果滿足了以上情況的任意一種,那這個物件JVM就會判定為逃逸,對以上兩種情況舉例,樣例來源於:https://zhuanlan.zhihu.com/p/59215831
public class EscapeTest {
public static Object globalVariableObject;
public Object instanceObject;
public void globalVariableEscape(){
globalVariableObject = new Object(); // 靜態變數,外部執行緒可見,發生逃逸
}
public void instanceObjectEscape(){
instanceObject = new Object(); // 賦值給堆中例項欄位,外部執行緒可見,發生逃逸
}
public Object returnObjectEscape(){
return new Object(); // 返回例項,外部執行緒可見,發生逃逸
}
public void noEscape(){
Object noEscape = new Object(); // 僅建立執行緒可見,物件無逃逸
}
}
Java的逃逸分析只能發生在即時編譯(JIT)期,為什麼不能在靜態編譯(javac)中?
參考R大回答:https://www.zhihu.com/questio...
總結來說是可以發生在靜態編譯期的,但是Java的分離編譯和動態載入使得前期的靜態編譯的逃逸分析比較困難或收益較少,所以目前Java的逃逸分析只發在JIT的即時編譯中,因為收集到足夠的執行資料JVM可以更好的判斷物件是否發生了逃逸。
JVM開啟逃逸分析以後的優勢?
Java8+預設是開啟的,-XX:+DoEscapeAnalysis
棧上分配,虛擬機器引數:
-XX:+PrintGC -Xms5M -Xmn5M -XX:+DoEscapeAnalysis
這種優化可以降低垃圾收集器執行的頻率,這樣每當方法出棧,物件記憶體隨之釋放。
public static void main(String[] args) { for(int i = 0; i < 5000000; i++) { createObject(); } } public static void createObject() { new Object(); }
同步消除
- 如果發現某個物件只能從一個執行緒可訪問,那麼在這個物件上的操作可以不需要同步。
標量替換
- 如果某個物件的訪問方式不要求該物件是一個連續的記憶體結構,那麼物件的部分(或全部)可以不儲存在記憶體,而是儲存在CPU暫存器中。簡單來說就是把物件分解成一個個基本型別,並且記憶體分配不再是分配在堆上,而是分配在棧上。這樣的好處有,一、減少記憶體使用,因為不用生成物件頭。 二、程式記憶體回收效率高,並且GC頻率也會減少。