Java記憶體外洩問題實例分析
Java記憶體外洩問題
所謂記憶體外洩就是指一個不再被程式便用的物件或變數一直被佔據在記憶體中。
Java 中有垃圾回收機制,它可以保證一物件不再被引用的時候,也就是物件變成了孤兒的時候,物件就會自動被垃圾回收器從記憶體中清除掉。
既然java有垃圾回收機制,為什麼還會有記憶體洩漏的問題呢?
無非,就是有些對象,無法被垃圾回收器處理,導致這些對像一直佔用JVM內存,那不就導致內存洩漏了嘛。
由於Java 使用有向圖的方式進行垃圾回收管理,可以消除引用循環的問題,例如有兩個對象,相互引用, 只要它們和根進程不可達的,那麼GC也是可以回收它們的,例如下面的程式碼可以看到這種情況的記憶體回收。
import java. io.IOException; public class GarbageTest { public static void main(String[] args) throws IOException { try { // TODO Auto-generated method stub gcTest(); } catch (IOException e) { e.printStackTrace(); } System.out.println("has exited gcTest!"); System.in.read(); System.in.read(); System.out.println("out begin gc!"); for (int i = 0; i < 100; i++) { System.gc(); System.in.read(); System.in.read(); } } private static void gcTest() throws IOException { System.in.read(); System.in.read(); Person p1 = new Person(); System.in.read(); System.in.read(); Person p2 = new Person(); p1.setMate(p2); p2.setMate(p1); System.out.println("before exit gctest!"); System.in.read(); System.in.read(); System.gc(); System.out.println("exit gctest!"); } private static class Person { byte[] data = new byte[20000000]; Person mate = null; public void setMate(Person other) { mate = other; } } }
Java 中的記憶體洩露的情況: 長生命週期的物件持有短生命週期物件的引用就很可能發生記憶體洩露,儘管短室命週期物件已經不再需要,但是因為長生命週期對象持有它的引用而導致不能被回收,這就是Java 中內存洩露的發室場景,通俗地說,就是程序員可能創建了一個對象,以後一直不再使用這個對象,這個對象卻一直被引用,即這個物件無用但是卻無法被垃圾回收器回收的,這就是java 中可能出現記憶體洩漏的情況。
例如,快取系統,我們載入了一個物件放在快取中(例如放在一個全域map物件中),然後一直不再使用它,這個物件一值被快取引用, 但卻不再被便用。
檢查Java 中的記憶體洩露,一定要讓程式將各種分支情況都完整執行到程式結束,然後看某個物件是否被使用過,如果沒有,則才能判定這個物件屬於記憶體洩露。
如果一個外部類別的實例物件的方法傳回了一個內部類別的實例對象,這個內部類別物件被長期引用了,即使那個外部類別實例物件不再被使用,但由於內部類別持久外部類別的實例對象,這個外部類別物件將不會被垃圾回收,這也會造成記憶體洩漏.
下面內容來自於網路上(主要特點就是清空堆疊中的某個元素,並不是徹底把它從陣列中拿掉,而是把儲存的總數減少,本人寫得可以比這個好,在拿掉某個元素時,順便也讓它從數組中消失,將那個元素所在的位置的值設為null 即可)
我實在想不到比那個堆疊更經典的例子了,以致於我還要引用別人的例子,下面的例子不是我想到的,是書上看到的,當然如果沒有在書上看到,可能過一段時間我自己也想的到,可是那時我說是我自己想到的也沒人相信的。
public class Stack { private Object[] elements = new Object[10]; private int size = 0; public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if (elements.length == size) { Object[] oldElements = elements; elements = new Object[2 * elements.length + 1]; System.arraycopy(oldElements, 0, elements, 0, size); } } }
上面的原理應該很簡單,假如堆錢加了10 個元素,然後全部彈出來,雖然堆錢是空的,沒有我們要的東西,但是這是個物件是無法回收的,這個才符合了記憶體外洩的兩個條件無用,無法回收。但就是存在這樣的東西也不一定會導致什麼樣的後果,如果這個堆錢用的比較少,也就浪費了幾個K 內存而己,反正我們的內存都上G 了,哪裡會有什麼影響,再說這個東西很快就會被回收的,有什麼關係。下面看兩個例子。
class Bad { public static Stack s = new Stack(); static { s.push(new Object()); s.pop(); //这里有一个对象发生内存泄露 s.push(new Object());//上面的对象可以被回收了,等于是自愈了 } }
因為是static ,就一直存在到程式退出,但我們也可以看到它有自癒功能,就是說如果你的Stack 最多有100 個對象,那麼最多也就只有100 個對象無法被回收, 其實這個應該很容易理解,Stack 內部持有100 個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的進取,以前的引用自然消失!
記憶體外洩的另一個情況: 當一個物件儲存進HashSet 集合中以後,就不能修改這個物件中的那些參與計算哈希值的欄位了,否則,物件修改後的晗希值與最初儲存進HashSet 集合中時的哈希值就不同了,在這種情況下,即使在contains 方法使用該對象的當前引用作為的參數去HashSet 集合中檢索對象, 也將返回找不到對象的結果,這也會導致無法從HashSet 集合中單獨刪除當前對象,造成記憶體外洩。
附:記憶體外洩的典型情況
(1) 資料結構造成的短暫記憶體外洩問題,看下面的程式碼
public class Stack{ private Object[] element=new Object[10]; private int size=0; public void push(Object ele){ ensureCapacity(); element[size++]=ele; } public Object pop(){ if(size==0) throw new EmptyStackException(); return element[--size]; //短暂造成内存泄露 } private void ensureCapacity(){ if(element.length==size){ Object[] oldElement=element; element=new Object[size*2+1]; System.arraycopy(oldElement,0,element,0,size); } } }
上面的程式碼每一次pop()的時候,Stack都會彈出一個元素,在沒有加入新元素之前,實際上仍然有一個引用element[x]指向了這個已經彈出的對象,因此GC是不會對其進行垃圾回收的。只有push()新元素的時候使得element[x]=newObject,才會使得先前建立的物件有可能被回收。應該把上面的pop()方法改成下面的程式碼就安全多了:
public Object pop(){ if(element.length==size) throws EmptyStackException(); Object o=element[--size]; elements[size]=null; //使得GC有机会回收这个对象 return o; }
以上是Java記憶體外洩問題實例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

Java是熱門程式語言,適合初學者和經驗豐富的開發者學習。本教學從基礎概念出發,逐步深入解說進階主題。安裝Java開發工具包後,可透過建立簡單的「Hello,World!」程式來實踐程式設計。理解程式碼後,使用命令提示字元編譯並執行程序,控制台上將輸出「Hello,World!」。學習Java開啟了程式設計之旅,隨著掌握程度加深,可創建更複雜的應用程式。
