【Java】變量聲明在循環(huán)體內(nèi)還是循環(huán)體外你用哪一個(gè)?
引言
最近刷知乎的時(shí)候看到一個(gè)比較有意思的問題,變量聲明在循環(huán)體內(nèi)還是循環(huán)體外?這個(gè)問題有人認(rèn)為應(yīng)該定義循環(huán)體外,不應(yīng)該定義在循環(huán)體內(nèi)。很多java代碼優(yōu)化建議都有這么一條建議:循環(huán)內(nèi)不要不斷創(chuàng)建對(duì)象引用 例如:
for (int i = 1; i <= count; i++){
Object obj = new Object();
}
這種做法會(huì)導(dǎo)致內(nèi)存中有count份Object對(duì)象引用存在,count很大的話,就耗費(fèi)內(nèi)存了,建議為改為:
Object obj = null;
for (int i = 0; i <= count; i++) {
obj = new Object();
}
這樣的話,內(nèi)存中只有一份Object對(duì)象引用,每次new Object()的時(shí)候,Object對(duì)象引用指向不同的Object罷了,但是內(nèi)存中只有一份,這樣就大大節(jié)省了內(nèi)存空間了。這條建議應(yīng)該也出現(xiàn)過在很多公司的代碼規(guī)范上了吧。下面我們就來(lái)分析下變量聲明在循環(huán)體內(nèi)和變量聲明循環(huán)體外的情況。
效率對(duì)比
首先我們先來(lái)看看寫在循環(huán)體內(nèi)和詢環(huán)體外的效率比對(duì),測(cè)試代碼如下:
/**
* @author: 公眾號(hào)【java金融】
* @Date:
* @Description:
*/
@BenchmarkMode(Mode.AverageTime) // 測(cè)試完成時(shí)間
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2) // 預(yù)熱 2 輪,每次 1s
@Measurement(iterations = 5) // 測(cè)試 5 輪,每次 1s
@Fork(1) // fork 1 個(gè)線程
@State(Scope.Thread)
public class ForEachBenchMark {
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(ForEachBenchMark.class.getSimpleName())
.result("result.json")
.resultFormat(ResultFormatType.JSON).build();
new Runner(opt).run();
}
@Param(value = {"10", "50", "100"})
private int length;
/**
* 循環(huán)體外創(chuàng)建對(duì)象
* @param blackhole
*/
@Benchmark
public void outsideLoop(Blackhole blackhole) {
Object object = null;
for (int i = 0; i < length; i++) {
object = new Object();
blackhole.consume(object);
}
}
/**
* 循環(huán)體內(nèi)創(chuàng)建對(duì)象
* @param blackhole
*/
@Benchmark
public void insideLoop(Blackhole blackhole) {
for (int i = 0; i < length; i++) {
Object object = new Object();
blackhole.consume(object);
}
}
}
測(cè)試結(jié)果如下:
Benchmark (length) Mode Cnt Score Error Units
ForEachBenchMark.insideLoop 10 avgt 5 58.629 ± 8.857 ns/op
ForEachBenchMark.insideLoop 50 avgt 5 293.726 ± 1.856 ns/op
ForEachBenchMark.insideLoop 100 avgt 5 587.185 ± 40.424 ns/op
ForEachBenchMark.outsideLoop 10 avgt 5 59.563 ± 5.057 ns/op
ForEachBenchMark.outsideLoop 50 avgt 5 305.829 ± 27.476 ns/op
ForEachBenchMark.outsideLoop 100 avgt 5 584.853 ± 20.289 ns/op
我們可以發(fā)現(xiàn)不管在循環(huán)外創(chuàng)建對(duì)象和循環(huán)內(nèi)創(chuàng)建對(duì)象時(shí)間幾乎都是一樣的。
字節(jié)碼對(duì)比
下面我們準(zhǔn)備兩個(gè)測(cè)試類
public class InsideTest {
public static int count = 100;
public List<Object> insideLoop() {
List<Object> list = new ArrayList<>();
int n = 0;
for (; ; ) {
if (n > count) {
break;
}
Object o = new Object();
list.add(o);
}
Object b = 2;
return list;
}
}
public class OutsideTest {
public static int count = 100;
public List<Object> outsideLoop() {
List<Object> list = new ArrayList<>();
Object o = null;
int n = 0;
for (; ; ) {
if (n > count) {
break;
}
o = new Object();
list.add(o);
}
Object b = 2;
return list;
}
這兩個(gè)編譯后字節(jié)碼幾乎一模一樣,除了循環(huán)體外(OutsideTest )常量池多了一個(gè)Object o = null變量還有的話就是LocalVariableTable有點(diǎn)區(qū)別,變量在循環(huán)體內(nèi)的話公用了一個(gè)變量槽(o和b變量) outsideLoop在stack frame中定義了4個(gè)slot, 而intsideLoop只定義了3個(gè)slot 在outsideLoop中,變量o和b分別占用了不同的slot,在intsideLoop中,變量o和b復(fù)用一個(gè)slot。所以outsideLoop的stack frame比intsideLoop多占用1個(gè)solt內(nèi)存。執(zhí)行以下命令就可以找到字節(jié)碼中的LocalVariableTable。
javac -g OutsideTest.java
javap -v OutsideTest.class
LocalVariableTable:
Start Length Slot Name Signature
28 8 3 o Ljava/lang/Object;
0 46 0 this Lcom/workit/autoconfigure/autoconfigure/controller/InsideTest;
8 38 1 list Ljava/util/List;
10 36 2 n I
44 2 3 b Ljava/lang/Object;
LocalVariableTable:
Start Length Slot Name Signature
0 49 0 this Lcom/workit/autoconfigure/autoconfigure/controller/OutsideTest;
8 41 1 list Ljava/util/List;
10 39 2 o Ljava/lang/Object;
12 37 3 n I
47 2 4 b Ljava/lang/Object;
這是比較極端的情況下有1個(gè)solt的差距,如果把上述的代碼 Object b = 2;就不會(huì)存在solt復(fù)用了。
總結(jié)
整體看下來(lái)貌似內(nèi)存和效率都差不多。從“「局部變量作用域最小化」”原則上來(lái)說,變量聲明在循環(huán)體內(nèi)更合適一點(diǎn),這樣代碼的閱讀性更好。
結(jié)束
由于自己才疏學(xué)淺,難免會(huì)有紕漏,假如你發(fā)現(xiàn)了錯(cuò)誤的地方,還望留言給我指出來(lái),我會(huì)對(duì)其加以修正。 如果你覺得文章還不錯(cuò),你的轉(zhuǎn)發(fā)、分享、贊賞、點(diǎn)贊、留言就是對(duì)我最大的鼓勵(lì)。 感謝您的閱讀,十分歡迎并感謝您的關(guān)注。巨人肩膀 https://www.zhihu.com/question/31751468
-End-
最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

面試題】即可獲取
