1. 堆與棧優(yōu)缺點(diǎn)
棧:
- 訪問速度快,僅次于寄存器;
- 但存儲(chǔ)在棧中的數(shù)據(jù)大小與生命周期必須是確定的。
堆:
- 由于要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,所以數(shù)據(jù)訪問速度較慢;
- 但可以動(dòng)態(tài)分配內(nèi)存,生存周期也不需要事先告訴編譯器,靈活。
由于Java堆區(qū)是GC的重點(diǎn)回收區(qū)域,所以GC極有可能會(huì)在大內(nèi)存的使用和頻繁進(jìn)行垃圾回收構(gòu)成上稱為系統(tǒng)性能瓶頸。為解決這個(gè)問題,JVM的設(shè)計(jì)者開始考慮是否一定需要將對(duì)象實(shí)例存儲(chǔ)到Java堆區(qū)內(nèi)。
2. 逃逸
如果一個(gè)對(duì)象的指針被多個(gè)方法或線程引用,那我們可以稱這個(gè)指針發(fā)生了逃逸。
指針逃逸示例:
public class G {
public static B b;
public void globalVariablePointerEscape () { //賦值給全局變量,發(fā)生逃逸
b = new B();
}
public B methodPointerEscape () { //方法返回值,發(fā)生逃逸
return new B();
}
public void instancePassPointerEscape () { //實(shí)例引用發(fā)生逃逸
methodPointerEscape().printClassName(this);
}
}
class B{
public void printClassName(G g) {
System.out.println(g.getClass().getName());
}
}
3. 逃逸分析對(duì)JVM優(yōu)化
我們可以采用逃逸分析原理對(duì)JVM進(jìn)行優(yōu)化,即針對(duì)棧的重新分配方式。首先需要分析并且找到未逃逸的變量,將變量類的實(shí)例內(nèi)存直接在棧里分配(無(wú)須進(jìn)入堆),分配完成后,繼續(xù)在調(diào)用棧內(nèi)執(zhí)行,最后線程結(jié)束,??臻g被回收,局部變量對(duì)象也被回收。通過這種優(yōu)化方式,??臻g直接作為臨時(shí)對(duì)象的存儲(chǔ)介質(zhì),從而減少了臨時(shí)對(duì)象在堆內(nèi)的分配數(shù)量,減輕了堆內(nèi)GC的壓力。
實(shí)例代碼:
public void my_method() {
V v = new V();
//use v
......
v = null;
}
在這個(gè)方法中創(chuàng)建的局部對(duì)象被賦給了v,但是沒有返回,沒有賦給全局變量等操作,因此這個(gè)對(duì)象是沒有逃逸的,是可以運(yùn)行時(shí)在棧上分配和銷毀的對(duì)象。沒有發(fā)生逃逸的對(duì)象,由于生命周期都在一個(gè)方法體內(nèi),因此它們可以在運(yùn)行時(shí)在棧上分配和銷毀。
JDK1.7開始支持對(duì)象的棧分配和逃逸分析機(jī)制。這樣的機(jī)制除了能將堆分配對(duì)象變成棧分配對(duì)象以外,逃逸分析還有其他兩個(gè)優(yōu)化應(yīng)用:
- 同步消除。線程同步的代價(jià)是相當(dāng)高的,同步的后果是降低并發(fā)性和性能。逃逸分析可以判斷出某個(gè)對(duì)象是否始終只被一個(gè)線程訪問,如果只被一個(gè)線程訪問,那么對(duì)該對(duì)象的同步操作就可以轉(zhuǎn)化成沒有同步保護(hù)的操作,這樣就能大大提高并發(fā)性和性能。
- 矢量替代。逃逸分析方法如果發(fā)現(xiàn)對(duì)象的內(nèi)存存儲(chǔ)結(jié)構(gòu)不需要連續(xù)進(jìn)行的話,就可以將對(duì)象的部分甚至全部都保存在CPU寄存器內(nèi),這樣能大大加快訪問速度。