何爲內存溢出,何爲內存泄露

二者不可告人的關係

內存泄漏定義(memory leak):一個不再被程序使用的對象或變量還在內存中佔有存儲空間。

一次內存泄漏似乎不會有大的影響,但內存泄漏堆積後的後果就是內存溢出。

內存溢出 out of memory :指程序申請內存時,沒有足夠的內存供申請者使用,或者說,給了你一塊存儲int類型數據的存儲空間,但是你卻存儲long類型的數據,那麼結果就是內存不夠用,此時就會報錯OOM,即所謂的內存溢出。

二者的關係:

  1. 內存泄漏的堆積最終會導致內存溢出
  2. 內存溢出就是你要的內存空間超過了系統實際分配給你的空間,此時系統相當於沒法滿足你的需求,就會報內存溢出的錯誤。
  3. 內存泄漏是指你向系統申請分配內存進行使用(new),可是使用完了以後卻不歸還(delete),結果你申請到的那塊內存你自己也不能再訪問(也許你把它的地址給弄丟了),而系統也不能再次將它分配給需要的程序。就相當於你租了個帶鑰匙的櫃子,你存完東西之後把櫃子鎖上之後,把鑰匙丟了或者沒有將鑰匙還回去,那麼結果就是這個櫃子將無法供給任何人使用,也無法被垃圾回收器回收,因爲找不到他的任何信息。
  4. 內存溢出:一個盤子用盡各種方法只能裝4個果子,你裝了5個,結果掉倒地上不能吃了。這就是溢出。比方說棧,棧滿時再做進棧必定產生空間溢出,叫上溢,棧空時再做退棧也產生空間溢出,稱爲下溢。就是分配的內存不足以放下數據項序列,稱爲內存溢出。說白了就是我承受不了那麼多,那我就報錯,

由於java的JVM引入了垃圾回收機制,垃圾回收器會自動回收不再使用的對象,瞭解JVM回收機制的都知道JVM是使用引用計數法和可達性分析算法來判斷對象是否是不再使用的對象,本質都是判斷一個對象是否還被引用。那麼對於這種情況下,由於代碼的實現不同就會出現很多種內存泄漏問題(讓JVM誤以爲此對象還在引用中,無法回收,造成內存泄漏)。

1、靜態集合類,如HashMap、LinkedList等等。如果這些容器爲靜態的,那麼它們的生命週期與程序一致,則容器中的對象在程序結束之前將不能被釋放,從而造成內存泄漏。簡單而言,長生命週期的對象持有短生命週期對象的引用,儘管短生命週期的對象不再使用,但是因爲長生命週期對象持有它的引用而導致不能被回收。

2、各種連接,如數據庫連接、網絡連接和IO連接等。在對數據庫進行操作的過程中,首先需要建立與數據庫的連接,當不再使用時,需要調用close方法來釋放與數據庫的連接。只有連接被關閉後,垃圾回收器纔會回收對應的對象。否則,如果在訪問數據庫的過程中,對Connection、Statement或ResultSet不顯性地關閉,將會造成大量的對象無法被回收,從而引起內存泄漏。

3、變量不合理的作用域。一般而言,一個變量的定義的作用範圍大於其使用範圍,很有可能會造成內存泄漏。另一方面,如果沒有及時地把對象設置爲null,很有可能導致內存泄漏的發生。

<pre style="-webkit-tap-highlight-color: transparent; box-sizing: border-box; font-family: Consolas, Menlo, Courier, monospace; font-size: 16px; white-space: pre-wrap; position: relative; line-height: 1.5; color: rgb(153, 153, 153); margin: 1em 0px; padding: 12px 10px; background: rgb(244, 245, 246); border: 1px solid rgb(232, 232, 232); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class UsingRandom {
private String msg;
public void receiveMsg(){
readFromNet();// 從網絡中接受數據保存到msg中
saveDB();// 把msg保存到數據庫中
}
}
</pre>

如上面這個僞代碼,通過readFromNet方法把接受的消息保存在變量msg中,然後調用saveDB方法把msg的內容保存到數據庫中,此時msg已經就沒用了,由於msg的生命週期與對象的生命週期相同,此時msg還不能回收,因此造成了內存泄漏。

實際上這個msg變量可以放在receiveMsg方法內部,當方法使用完,那麼msg的生命週期也就結束,此時就可以回收了。還有一種方法,在使用完msg後,把msg設置爲null,這樣垃圾回收器也會回收msg的內存空間。

4、內部類持有外部類,如果一個外部類的實例對象的方法返回了一個內部類的實例對象,這個內部類對象被長期引用了,即使那個外部類實例對象不再被使用,但由於內部類持有外部類的實例對象,這個外部類對象將不會被垃圾回收,這也會造成內存泄露。

5、改變哈希值,當一個對象被存儲進HashSet集合中以後,就不能修改這個對象中的那些參與計算哈希值的字段了,否則,對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不同了,在這種情況下,即使在contains方法使用該對象的當前引用作爲的參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會導致無法從HashSet集合中單獨刪除當前對象,造成內存泄露

內存泄漏的解決方法:

內存泄漏也許是因爲活動已經被使用完畢,但是仍然在其他地方被引用,導致無法對其進行回收。我們只需要給對活動進行引用的類獨立出來或者將其變爲靜態類,該類隨着活動的結束而結束,也就沒有了當活動結束但仍然還被其他類引用的情況。

資源性對象在不使用的時候,應該調用它的close()函數將其關閉掉。。

集合容器中的內存泄露 ,我們通常把一些對象的引用加入到了集合容器(比如ArrayList)中,當我們不需要該對象時,並沒有把它的引用從集合中清理掉,這樣這個集合就會越來越大。如果這個集合是static的話,那情況就更嚴重了。

需要在退出程序之前,將集合裏的東西clear,然後置爲null,再退出程序。

WebView造成的泄露,當我們不使用WebView對象時,應該調用它的destory()函數來銷燬它,並釋放其佔用的內存,否則其長期佔用的內存也不能被回收,從而造成內存泄露。

我們應該爲WebView另外開啓一個進程,通過AIDL與主線程進行通信,WebView所在的進程可以根據業務的需要選擇合適的時機進行銷燬,從而達到內存的完整釋放。

內存溢出的原因及解決方法:

內存溢出原因:

1.內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;

2.集合類中有對對象的引用,使用完後未清空,產生了堆積,使得JVM不能回收;

3.代碼中存在死循環或循環產生過多重複的對象實體;

4.使用的第三方軟件中的BUG;

5.啓動參數內存值設定的過小

內存溢出的解決方案:

第一步,修改JVM啓動參數,直接增加內存。(-Xms,-Xmx參數一定不要忘記加。)

第二步,檢查錯誤日誌,查看“OutOfMemory”錯誤前是否有其 它異常或錯誤。

第三步,對代碼進行走查和分析,找出可能發生內存溢出的位置。

如何避免內存泄漏?

1、在涉及使用Context時,對於生命週期比Activity長的對象應該使用Application的Context。凡是使用Context優先考慮Application的Context,當然它並不是萬能的,對於有些地方則必須使用Activity的Context。對於Application,Service,Activity三者的Context的應用場景如下:

其中,NO1表示Application和Service可以啓動一個Activity,不過需要創建一個新的task任務隊列。而對於Dialog而言,只有在Activity中才能創建。除此之外三者都可以使用。

2、對於需要在靜態內部類中使用非靜態外部成員變量(如:Context、View ),可以在靜態內部類中使用弱引用來引用外部類的變量來避免內存泄漏。

3、對於不再需要使用的對象,顯示的將其賦值爲null,比如使用完Bitmap後先調用recycle(),再賦爲null。

4、保持對對象生命週期的敏感,特別注意單例、靜態對象、全局性集合等的生命週期。

5、對於生命週期比Activity長的內部類對象,並且內部類中使用了外部類的成員變量,可以這樣做避免內存泄漏:

1將內部類改爲靜態內部類

2靜態內部類中使用弱引用來引用外部類的成員變量

其中,NO1表示Application和Service可以啓動一個Activity,不過需要創建一個新的task任務隊列。而對於Dialog而言,只有在Activity中才能創建。除此之外三者都可以使用。

2、對於需要在靜態內部類中使用非靜態外部成員變量(如:Context、View ),可以在靜態內部類中使用弱引用來引用外部類的變量來避免內存泄漏。

3、對於不再需要使用的對象,顯示的將其賦值爲null,比如使用完Bitmap後先調用recycle(),再賦爲null。

4、保持對對象生命週期的敏感,特別注意單例、靜態對象、全局性集合等的生命週期。

5、對於生命週期比Activity長的內部類對象,並且內部類中使用了外部類的成員變量,可以這樣做避免內存泄漏:

1將內部類改爲靜態內部類

2靜態內部類中使用弱引用來引用外部類的成員變量

如何檢查和分析內存泄漏?

因爲內存泄漏是在堆內存中,所以對我們來說並不是可見的。通常我們可以藉助MAT、LeakCanary等工具來檢測應用程序是否存在內存泄漏。

1、MAT是一款強大的內存分析工具,功能繁多而複雜。

2、LeakCanary則是由Square開源的一款輕量級的第三方內存泄漏檢測工具,當檢測到程序中產生內存泄漏時,它將以最直觀的方式告訴我們哪裏產生了內存泄漏和導致誰泄漏了而不能被回收。

總結

1.關係:內存泄露最終會導致內存溢出,由於系統中的內存是有限的,如果過度佔用資源而不及時釋放,最後會導致內存不足,從而無法給所需要存儲的數據提供足夠的內存,從而導致內存溢出。導致內存溢出也可能是由於在給數據分配大小時沒有根據實際要求分配,最後導致分配的內存無法滿足數據的需求,從而導致內存溢出。

2.區別:內存泄露是由於GC無法及時或者無法識別可以回收的數據進行及時的回收,導致內存的浪費;內存溢出是由於數據所需要的內存無法得到滿足,導致數據無法正常存儲到內存中。內存泄露的多次表現就是會導致內存溢出。
覺得文章不錯,關注下我的微信公衆號吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章