今天面試了一家公司的java開發方面的實習生,被問到一個問題:如何處理java中的內存泄露問題,保證java的虛擬機內存不會被爆掉,當時其實覺得面試官的問題有點泛,所以也沒有很好領會他的意思,答案也不是很准確,后來回去查了下資料,大概明白面試官要問的東西是什么(尷尬,才反應過來),然后,也特地簡單總結下java內存溢出的相關內容,以備之后復習。
一、什么情況下會java內存泄露?
java不是有GC嗎?為毛還會內存泄露?之前我也一直以為,java有垃圾回收器在,估計內存泄露的情況一般不會發生吧?后來發現並非如此,先看以下代碼:
//內存泄露的簡單例子 //首先,要明白,GC它回收的是不可到達的對象,但是,在static 的集合類中,引用可以到達,但是卻有可能對象已經不用了 //首先定義一個靜態變量 public static ArrayList<Object> list = new ArrayList<Object>(); public void stackOverExam(Object object){ //當非靜態變量被static變量持有引用的時候,容易發生內存泄露,因為object是一直被list引用着的 list.add(object); object = null;//這里設置為null並沒有達到釋放object引用對應對象的效果,畢竟list還是持有引用 }
通過上面的代碼,估計大家也可以看到:由於static指向的對象是不能被垃圾回收器回收的,所以,間接地object也無法回收,當業務對象很大而且很多時,便有內存泄露的風險了,所以我們可以總結到如下規則:當全局的靜態變量持有局部變量(或者說,大范圍的變量持有小范圍變量而且小范圍變量消耗內存較大、數目較多時),程序便有內存泄露的風險,一般來說,類似的例子還有,單例模式中的對象,模塊之間的調用,內部類的引用被持有等
二、如何解決?
經過百度一番,其實發覺解決的思路大概和下面這方面的知識有關:java的對象引用有以下幾類:強引用,軟引用,弱引用。具體看下圖
解決剛說的內存泄露問題,主要運用的就是顯式聲明引用的類型,首先簡單解釋下相關的知識:
1、引用的類型:強,軟,弱。普通的java引用,也是我們最一般的java引用對象,便是強引用,它直接影響垃圾回收的進行(有強引用指向對應的對象,GC便不可回收該對象,即使內存不夠用);軟引用,和強引用的主要區別是:它會影響GC對對象的回收工作,但是,當虛擬機的內存不夠用時,軟引用所指向對象(當然,不能同時有強引用指向對象)會被強制回收;弱引用就等級更低,它無法影響GC對對象的垃圾回收工作,只能引用並訪問對象。具體三種引用可以由下面的代碼例子說明:
//軟引用和弱引用的使用例子 public void referenceExam(){ Object object = new Object();//這個普通object引用就是一個強引用 //軟引用通過SoftReference創建一個軟引用 SoftReference<Object> softObjReference = new SoftReference<Object>(object); //通過get方法獲取真正的對象引用 softObjReference.get(); //弱引用通過WeakReference來關聯一個對象 WeakReference<Object> weakObjReference = new WeakReference<Object>(object); weakObjReference.get();//這個方法返回了真實的對象引用,當沒有強引用或者弱引用指向它時,返回為null }
2、對於防止內存泄露以及與垃圾回收有關的引用強弱類型,我們可以歸納出下面的特點:
A、當我們在高速緩存中緩存較大的對象例如緩存圖片等以提高讀取速度的時候(針對大內存對象問題),可以將相關的對象設置為軟引用對象,這樣保證在內存不夠用的時候GC可以進行回收工作。
B、當我們完全不像由於當前對對象的引用而影響對象的其他模塊原來設定的GC回收時期,我們可以采用弱引用(相當於只可訪問數據而訪問不會對GC對對象的回收造成影響)
C、當然,java還有一種叫做虛引用的引用類型變量,該變量其實現實中用得不多,主要用來跟蹤對象GC過程,所以這里不詳細講。
最后,至於什么時候要設置對象為弱引用(或者軟引用),個人覺得還是要根據具體的業務進行對象使用的判斷,如果全局對象過大,有可能在程序中造成泄露問題,或者說我們不想在不清楚對象何時可以被回收(工廠通過接口提供對象給客戶)時,可以采用軟引用或者弱引用進行對象訪問。
當然,其實內存泄露是個很大的主題,這里僅僅是討論了一小部分內容而已,各位有更多的見解也歡迎交流!