高效使用內(nèi)存,這些你得知道
?之后更新文章的頻率會(huì)變高,不過可能不會(huì)只更新技術(shù)文章了
最近也在閱讀“金字塔原理“和”高效能人士的七個(gè)習(xí)慣“這兩本書,之后也會(huì)做個(gè)讀書心得分享
?
前言
首先有一個(gè)結(jié)論:
「減少堆內(nèi)存的使用可以更高效使用內(nèi)存」
?這句話怎么理解呢?
?
堆內(nèi)存用的越少,堆被填滿的幾率就越低,新生代回 收的次數(shù)更少,對(duì)象的晉升年齡也就不會(huì)很頻繁地增加,這意味著對(duì)象被提升到老年代的 可能性也降低了,因此, Full GC會(huì)減少,降低了GC發(fā)生的頻率
「下面將介紹幾種減少內(nèi)存使用的方式」
減少對(duì)象大小
對(duì)象會(huì)占用一定數(shù)量的堆內(nèi)存,所以要減少內(nèi)存使用,最簡單的方式就是讓對(duì)象小一些
減少對(duì)象大小有兩種方式:減少實(shí)例變量的個(gè)數(shù),或者減少實(shí)例變量的大小
注意這里的減少變量大小主要是對(duì)每個(gè)對(duì)象選擇更小的類型,避免空間的浪費(fèi)
「同時(shí)對(duì)于對(duì)象大小的計(jì)算,有幾個(gè)注意點(diǎn):」
1.對(duì)象大小未必和你計(jì)算的一樣,因?yàn)閷?duì)象會(huì)被填充到 8 字節(jié)的整數(shù)倍
2.對(duì)象內(nèi)部即使為 null 的實(shí)例變量也會(huì)占用空間
延遲初始化
有時(shí)候需要采用延遲初始化來降低初始化類和創(chuàng)建對(duì)象的開銷
注意我們一般不會(huì)在線程安全的代碼上引入延遲初始化,這樣會(huì)增加同步操作的開銷
同時(shí)對(duì)于使用了線程安全對(duì)象的代碼,如果要采用延遲初始化,也應(yīng)該使用雙重檢查鎖,比如大家熟悉的單例的寫法:
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
private LazyDoubleCheckSingleton(){
}
public static LazyDoubleCheckSingleton getInstance() {
if (lazyDoubleCheckSingleton == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (lazyDoubleCheckSingleton == null) {
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazyDoubleCheckSingleton;
}
}
「盡早清理空值數(shù)據(jù)」
延遲初始化的對(duì)象如果一直不用的話,通過將變量的值設(shè)置為 null ,實(shí)現(xiàn)盡早清理, 從而使對(duì)象可以更快地被垃圾收集器回收
可以看看JDK 中 ArrayList 類的 remove() 方法的實(shí)現(xiàn):
public E remove(int index) {
//檢查下標(biāo)是否越界
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
//將index+1以及之后的元素向前移動(dòng)以為,覆蓋被刪除的值
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//將最后一個(gè)位置的元素清空
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
關(guān)鍵就是對(duì)于不需要再引用的元素,應(yīng)該主動(dòng)將其設(shè)置為 null
不可變對(duì)象
「使用不可變的對(duì)象常量可以很好的減少空間的浪費(fèi)」
在 Java 中,很多對(duì)象類型都是不可變的
包括那些有相應(yīng)的基本類型的類,如 Integer 、 Double 和 Boolean 等, 以及其他一些基于數(shù)值的類型, 如 BigDecimal
最常見的 Java 對(duì)象當(dāng)屬不可變的 String
「舉個(gè)Boolean類的例子說說」
在 Java 中,其實(shí)只需要兩個(gè) Boolean 示例,一個(gè)表 示 true , 一個(gè)表示 false
Boolean 類有一個(gè) public 的構(gòu)造 器,應(yīng)用喜歡創(chuàng)建多少這類對(duì)象就能創(chuàng)建多少,即使它們和兩個(gè)Boolean 對(duì)象其 中之一是完全相同的
更好的設(shè)計(jì)方案應(yīng)該是,讓 Boolean 類只有一個(gè) private 的構(gòu)造器, 通過 static 方法根據(jù)其參數(shù)返回 Boolean.TRUE 或 Boolean.FALSE ,就可以防止它們占用應(yīng)用中額外的堆空間
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
字符串常量池
字符串是最常見的 Java 對(duì)象;應(yīng)用的堆中幾乎到處都是字符串
如果有大量的字符串是相同的,那很大一部分空間都是浪費(fèi)的
「所以有了字符串常量池的概念」
我們知道String 常見的創(chuàng)建方式有兩種,new String() 的方式和直接賦值的方式
直接賦值的方式會(huì)先去字符串常量池中查找是否已經(jīng)有此值,如果有則把引用地址直接指向此值,否則會(huì)先在常量池中創(chuàng)建,然后再把引用指向此值;而 new String()的方式一定會(huì)先在堆上創(chuàng)建一個(gè)字符串對(duì)象,然后再去常量池中查詢此字符串的值是否已經(jīng)存在,如果不存在會(huì)先在常量池中創(chuàng)建此字符串,然后把引用的值指向此字符串
String s1 = new String("Java");
String s2 = s1.intern();
String s3 = "Java";
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true

除此之外編譯器還會(huì)對(duì) String 字符串做一些優(yōu)化,例如以下代碼:
String s1 = "Ja" + "va";
String s2 = "Java";
System.out.println(s1 == s2);
雖然 s1 拼接了多個(gè)字符串,但對(duì)比的結(jié)果卻是 true,我們使用反編譯工具,看到的結(jié)果如下:
Compiled from "StringExample.java"
public class com.lagou.interview.StringExample {
public com.lagou.interview.StringExample();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String Java
2: astore_1
3: ldc #2 // String Java
5: astore_2
6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
9: aload_1
10: aload_2
11: if_acmpne 18
14: iconst_1
15: goto 19
18: iconst_0
19: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V
22: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 6
line 8: 22
}
從編譯代碼 #2 可以看出,代碼 “Ja”+”va” 被直接編譯成了 “Java” ,因此 s1==s2 的結(jié)果才是 true,這就是編譯器對(duì)字符串優(yōu)化的結(jié)果
最后
「喜歡的話,希望幫忙點(diǎn)贊,轉(zhuǎn)發(fā)下哈,謝謝」
微信搜索:月伴飛魚,交個(gè)朋友
公眾號(hào)后臺(tái)回復(fù)666,獲得免費(fèi)電子書籍,必讀書籍這里全都有
參考:
書籍:Java性能權(quán)威指南

社招一年半面經(jīng)分享(含阿里美團(tuán)頭條京東滴滴)

條件語句的多層嵌套問題優(yōu)化,助你寫出不讓同事吐槽的代碼

