1、盡早釋放無用對象的引用。
好的辦法是使用臨時(shí)變量的時(shí)候,讓引用變量在退出活動(dòng)域后,自動(dòng)設(shè)置為null,暗示垃圾收集器來收集該對象,防止發(fā)生內(nèi)存泄露。對于仍然有指針指向的實(shí)例,jvm就不會(huì)回收該資源,因?yàn)槔厥諘?huì)將值為null的對象作為垃圾,提高GC回收機(jī)制效率;
2、定義字符串應(yīng)該盡量使用 String
str=”hello”; 的形式 ,避免使用String str = new
String(“hello”);
的形式。因?yàn)橐褂脙?nèi)容相同的字符串,不必每次都new一個(gè)String。例如我們要在構(gòu)造器中對一個(gè)名叫s的String引用變量進(jìn)行初始化,把它設(shè)置為初始值,應(yīng)當(dāng)這樣做:
-
public class Demo {
-
private String s;
-
...
-
public Demo {
-
s = "Initial Value";
-
}
-
...
- }
而非
-
s = new String("Initial Value");
后者每次都會(huì)調(diào)用構(gòu)造器,生成新對象,性能低下且內(nèi)存開銷大,并且沒有意義,因?yàn)镾tring對象不可改變,所以對于內(nèi)容相同的字符串,只要一個(gè)String對象來表示就可以了。也就說,多次調(diào)用上面的構(gòu)造器創(chuàng)建多個(gè)對象,他們的String類型屬性s都指向同一個(gè)對象。
3、我們的程序里不可避免大量使用字符串處理,避免使用String,應(yīng)大量使用StringBuffer
,因?yàn)镾tring被設(shè)計(jì)成不可變(immutable)類,所以它的所有對象都是不可變對象,請看下列代碼;
-
String s = "Hello";
-
s = s + " world!";
在這段代碼中,s原先指向一個(gè)String對象,內(nèi)容是
"Hello",然后我們對s進(jìn)行了+操作,那么s所指向的那個(gè)對象是否發(fā)生了改變呢?答案是沒有。這時(shí),s不指向原來那個(gè)對象了,而指向了另一個(gè)
String對象,內(nèi)容為"Hello world!",原來那個(gè)對象還存在于內(nèi)存之中,只是s這個(gè)引用變量不再指向它了。
通過上面的說明,我們很容易導(dǎo)出另一個(gè)結(jié)論,如果經(jīng)常對字符串進(jìn)行各種各樣的修改,或者說,不可預(yù)見的修改,那么使用String來代表字符串的話會(huì)引起很大的內(nèi)存開銷。因?yàn)?String對象建立之后不能再改變,所以對于每一個(gè)不同的字符串,都需要一個(gè)String對象來表示。這時(shí),應(yīng)該考慮使用StringBuffer類,它允許修改,而不是每個(gè)不同的字符串都要生成一個(gè)新的對象。并且,這兩種類的對象轉(zhuǎn)換十分容易。
4、盡量少用靜態(tài)變量
,因?yàn)殪o態(tài)變量是全局的,GC不會(huì)回收的;
5、盡量避免在類的構(gòu)造函數(shù)里創(chuàng)建、初始化大量的對象
,防止在調(diào)用其自身類的構(gòu)造器時(shí)造成不必要的內(nèi)存資源浪費(fèi),尤其是大對象,JVM會(huì)突然需要大量內(nèi)存,這時(shí)必然會(huì)觸發(fā)GC優(yōu)化系統(tǒng)內(nèi)存環(huán)境;顯示的聲明數(shù)組空間,而且申請數(shù)量還極大。
以下是初始化不同類型的對象需要消耗的時(shí)間:
運(yùn)算操作 |
示例 |
標(biāo)準(zhǔn)化時(shí)間 |
本地賦值 |
i = n |
1.0 |
實(shí)例賦值 |
this.i = n |
1.2 |
方法調(diào)用 |
Funct()
|
5.9 |
新建對象 |
New Object() |
980 |
新建數(shù)組 |
New int[10] |
3100 |
從表1可以看出,新建一個(gè)對象需要980個(gè)單位的時(shí)間,是本地賦值時(shí)間的980倍,是方法調(diào)用時(shí)間的166倍,而若新建一個(gè)數(shù)組所花費(fèi)的時(shí)間就更多了。
這是一個(gè)案例想定供大家警戒
使用jspsmartUpload作文件上傳,運(yùn)行過程中經(jīng)常出現(xiàn)java.outofMemoryError的錯(cuò)誤,
檢查之后發(fā)現(xiàn)問題:組件里的代碼
m_totalBytes = m_request.getContentLength();
m_binArray = new byte[m_totalBytes];
問題原因是totalBytes這個(gè)變量得到的數(shù)極大,導(dǎo)致該數(shù)組分配了很多內(nèi)存空間,而且該數(shù)組不能及時(shí)釋放。解決辦法只能換一種更合適的辦法,至少是不會(huì)引發(fā)outofMemoryError的方式解決。
6、盡量在合適的場景下使用對象池技術(shù)
以提高系統(tǒng)性能,縮減縮減開銷,但是要注意對象池的尺寸不宜過大,及時(shí)清除無效對象釋放內(nèi)存資源,綜合考慮應(yīng)用運(yùn)行環(huán)境的內(nèi)存資源限制,避免過高估計(jì)運(yùn)行環(huán)境所提供內(nèi)存資源的數(shù)量。
7、大集合對象擁有大數(shù)據(jù)量的業(yè)務(wù)對象的時(shí)候,可以考慮分塊進(jìn)行處理 ,然后解決一塊釋放一塊的策略。
8、不要在經(jīng)常調(diào)用的方法中創(chuàng)建對象
,尤其是忌諱在循環(huán)中創(chuàng)建對象??梢赃m當(dāng)?shù)氖褂胔ashtable,vector
創(chuàng)建一組對象容器,然后從容器中去取那些對象,而不用每次new之后又丟棄。
9、一般都是發(fā)生在開啟大型文件或跟數(shù)據(jù)庫一次拿了太多的數(shù)據(jù),造成 Out Of Memory Error
的狀況,這時(shí)就大概要計(jì)算一下數(shù)據(jù)量的最大值是多少,并且設(shè)定所需最小及最大的內(nèi)存空間值。
10、盡量少用finalize函數(shù)
,因?yàn)閒inalize()會(huì)加大GC的工作量,而GC相當(dāng)于耗費(fèi)系統(tǒng)的計(jì)算能力。
11、盡量避免強(qiáng)制系統(tǒng)做垃圾內(nèi)存的回收System.gc();
12、要過濫使用哈希表
,有一定開發(fā)經(jīng)驗(yàn)的開發(fā)人員經(jīng)常會(huì)使用hash表(hash表在JDK中的一個(gè)實(shí)現(xiàn)就是HashMap)來緩存一些數(shù)據(jù),從而提高系統(tǒng)的運(yùn)行速度。比如使用HashMap緩存一些物料信息、人員信息等基礎(chǔ)資料,這在提高系統(tǒng)速度的同時(shí)也加大了系統(tǒng)的內(nèi)存占用,特別是當(dāng)緩存的資料比較多的時(shí)候。其實(shí)我們可以使用操作系統(tǒng)中的緩存的概念來解決這個(gè)問題,也就是給被緩存的分配一個(gè)一定大小的緩存容器,按照一定的算法淘汰不需要繼續(xù)緩存的對象,這樣一方面會(huì)因?yàn)檫M(jìn)行了對象緩存而提高了系統(tǒng)的運(yùn)行效率,同時(shí)由于緩存容器不是無限制擴(kuò)大,從而也減少了系統(tǒng)的內(nèi)存占用?,F(xiàn)在有很多開源的緩存實(shí)現(xiàn)項(xiàng)目,比如
ehcache、oscache等,這些項(xiàng)目都實(shí)現(xiàn)了FIFO、MRU等常見的緩存算法。
|