在座的各位程式設計師們,10月24號,今天還有沒有是坐在公司加班的?
小編估計肯定有,誰讓我們是程式設計師,就好比如保衛國家的戰士們,總是奮戰在戰場的前線,來守護老百姓的生活安寧。
不管咋樣,小編還是祝賀同胞們節日快樂,永無BUG、永遠青春、永不脫髮~~~
在小編剛開始學習Java語言的時候,然後知道了JVM,之前老師一直說Java中的範例物件就儲存在JVM中的堆區。
先給大家上一張JVM的記憶體模型圖,這圖應該很熟悉吧,應該對每一塊幹什麼用的多多少少也有了解個大概。
過了幾年,Java中的範例物件全部都是儲存在堆區的概念,已經被小編我深深的烙印在心中。
還記得小編在面試的時候,遇到過一個類似的問題:
面試官:你知道Java的範例物件是儲存在JVM中哪個區域麼?
小編:堆區。
面試官:那假設在方法中new了一個百萬個物件,也還是全部儲存在堆區嗎?
那麼關鍵的地方來了,當初對於年少無知的小編來說,哪會想那麼多,就死腦筋認為只要是範例物件就只會儲存在堆區中。
如果小夥伴你們也不知道答案,那麼帶著這個問題,來看實際操作一波吧。
public class Test_1 {
public static void main(String[] args) {
for (int i=0;i < 10000;i++){
create();
}
while (true);
}
public static void create(){
Test_1 test = new Test_1();
}
}
在程式碼中,迴圈了1w次,通過HSDB工具(HSDB可以檢視JVM在執行時資料區的內容),很明顯能看出,在堆區中,確實存在1w個Test_1物件,那麼現在改成把迴圈改成100w,再看看。
這下好了,count並沒有達到100w個,最初小編想的是是不是被GC回收了,然後列印GC紀錄檔也沒有發現。
那麼問題來了,剩下的物件跑哪兒去了???
其實這裡就涉及到一個知識點,叫做:逃逸分析,預設逃逸分析是開啟的,我們先把逃逸分析關閉掉,再試試。
把逃逸分析關閉之後,通過檢視物件,這下總算有100w個了。
用官方的話來說,逃逸分析是一種確定指標動態範圍的方法,可以分析在程式的哪些地方可以存取到指標。這裡的指標可以理解成java的範例物件的參照地址,而指標動態範圍可以理解為物件的存取修飾符(public、private等)。
上面那樣的解釋估計很多小夥伴都不懂,用程式碼來舉個例子。
在start方法中,new了一個Test_1物件,很明顯test_1這個物件是不是隻能在start()方法中使用,其他地方都不能夠使用。
這種物件就可以理解為不逃逸物件,因為它不能被其他地方存取到。
public class Test_1 {
public static void main(String[] args) {
start();
}
public static void start() {
Test_1 test_1 = new Test_1();
}
}
那現在將start方法改一下,把test_1這個物件返回出去,那麼這個物件就是可以理解為一個逃逸物件,因為它被返回出去了,只要是呼叫了start這個方法,就可以拿到這個物件,被其他地方存取到。
或者說當一個範例物件的參照指標被多個方法或執行緒參照時,我們稱這個指標發生了逃逸。
public static Test_1 start() {
Test_1 test_1 = new Test_1();
return test_1;
}
最後通過這種逃逸和不逃逸的現象,來進行分析,就稱之為:逃逸分析。
棧上分配就是基於逃逸分析這個分析,最後分析出來的一種優化方式,最大的好處應該是減少gc的壓力,把那些不逃逸的物件分配在棧上。這樣逃逸分析完後可以確定哪些變數可以分配在棧上,棧的分配比堆快,效能好。
這樣就像本文一開始演示的一樣,100w物件並沒有全部在堆中,而把逃逸分析關閉了,就都放在堆中了。
逃逸分析可以通過這個引數控制,-XX:+/-DoEscapeAnalysis,+就是表示開啟,-就是表示關閉。
下次如果再遇到問物件是不是全部放在堆上堆這種問題,可不要直接說都放在堆上。
當然棧上分配只是其中一種優化,還有包括標量替換、鎖消除等等,在逃逸分析的時候,如果你定義的物件的方法上有同步鎖,但在執行時,卻只有一個執行緒在存取,此時逃逸分析後的機器碼,會去掉同步鎖執行。
看著小編1024還在碼文章的份上,點個贊,點個贊,點個贊~~~~~~