標籤:
一般在java程式中,記憶體是個比較頭痛的話題。雖然jvm能夠通過GC機制很智能地回收資源,但是由於記憶體的釋放都是jvm在進行操作,不恰當的使用會導致java的程式記憶體持續增大,直至最終OOM(out of memery)
那麼,如何對java進行記憶體最佳化呢?一方面可以通過調整jvm的一些配置(記憶體,GC等),從jvm層最佳化配置;另一方面,從java程式角度,在代碼層次上進行最佳化。
近期,做了些在java程式方面記憶體最佳化實踐,積累了一些心得,總結如下:
對於資源消耗較大的對象(如資料庫連接,Stream,大的collection等),儘早進行釋放,還得考慮到異常情況
對於這種情況,就沒必要釋放obj了,因為在執行完func後,GC會自動回收資源
Public void func(){
Object obj =new Object();
……
Obj=null;
}
但是對於這類操作就有必要提前釋放了,特別是一些串連資源,大記憶體對象等
Public void func(){
Object obj =new Object();
……
Obj=null;
xxxx; // 耗時操作
}
而且對於資料庫資源/stream,在釋放的時候,要考慮到出現exception的情況,建議在finalize中釋放串連
public static Properties loadProperties(String fileName) throws IOException {
FileInputStream stream = new FileInputStream(fileName);
try {
Properties props = new Properties();
props.load(stream);
return props;
}
finally {
stream.close();
}
}
最佳化邏輯,避免不必要對象的建立,且盡量使用局部變數
由於局部變數是在stack上進行分配的,速度較快,且生命週期是固定的,同時可以最佳化程式的邏輯,避免一些不必要的邏輯的建立,如
A a = new A();
if(i==1){list.add(a);}
應該改為
if(i==1){
A a = new A();
list.add(a);
}
還有,比如:
FileInputStream stream = new FileInputStream(fileName);
if (xxx) {
return;
}
stream.xxx;
應該改成:
if (xxx) {
return;
}
FileInputStream stream = new FileInputStream(fileName);
stream.xxx;
最佳化迴圈邏輯,減少迴圈邏輯裡對象的建立和調用
for (int i = 0; i < xxx.size(); i++) {
xxx
}
這樣xxx.size()會被調用多次,效率較低,可以使用:
for (int i = 0, len = xxx.size(); i < len; i++) {
xxx
}
同樣要在迴圈裡面對於對象的建立要謹慎,如
for (int i = 0; i < 1000; ++i) {
Map myMap = new HashMap<>();
myMap.xxx;
}
應該改成
Map myMap = new HashMap<>();
for (int i = 0; i < 1000; ++i) {
myMap.clear();
myMap.XXX();
}
使用基本類型替代物件類型
比如使用int替代Integer,long替代Long等,數組替代vector等,減少對象建立。而且函數中,基本類型是存放於棧上建立和使用效率較高。
使用stringBuffer和stringBuilder替代多次String對象
因為String對象是定長的,任何關於String的變更操作都會導致新的String對象建立。因此可以在合適的時機使用stringBuferr或者stringBuilder替代String對象,單線程使用StringBuilder,多線程情況下使用StringBuffer。
提前分配stringBuffer,數組,array,vector等容量
對於需要連續分配的對象,最好先提前分配容量,一般預設的初始容量都比較小,如stringBuffer的容量是只有16,如果按照預設的容量很容易在容量滿了之後重新分配一塊大的資源,耗時費記憶體。因此可以通過提前分配資源的方式避免這個情況
緩衝經常要使用的對象和資料
對於經常需要使用的對象和資料,可以使用緩衝存起來(比如記憶體中的共用記憶體,或者redis,memcached等remote cache server),減少一些對象的重複建立計算代價
慎用exception
當建立一個異常時,需要收集一個棧跟蹤(stack track),這個棧跟蹤用於描述異常是在何處建立的。構建這些棧跟蹤時需要為運行時棧做一份快照,正是這一部分開銷很大。因此要慎用異常,一般而言,異常是只有當前處理不了的異常才往上層拋,否則可以通過函數傳回值(bool false,object=null)等方式向上呈現結果。
數組複製時,使用System.arraycopy()命令。
arrayCopy命令比迴圈複製數組快很多
java記憶體最佳化實踐