java內存泄露分析,Java 內存泄漏

本文目錄一覽:

java有內存溢出嗎?如果有是什麼情況?

內存溢出是指應用系統中存在無法回收的內存或使用的內存過多,最終使得程序運行要用到的內存大於虛擬機能提供的最大內存。

所以我們應該明確:存在內存溢出的因不一定導致內存溢出的果。。。

1。JAVA操作文本文件為什麼超過3萬行就內存益處啊?

PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));

//PrintWriter out = new PrintWriter(fileName);

//FileOutputStream out = new FileOutputStream(fileName);

while (rs.next()) {

for (int j = 1; j = totalColumn; j++) {

out.write(rs.getObject(j).toString());

out.write(“\t”);

}

out.write(“\n”);

out.flush();

}

}

我在代碼中 寫了 out.flush()用來刷新該流的緩衝; 可是當我的記錄數超過3W時就報了內存益處的問題了,難道JAVA不能邊讀邊寫嗎?還是out這個對象隨着指向的fileName文件的邊大佔用內存也大了嗎??到底怎麼來實現用JAVA寫更多的數據而不內存益處呢

答案是:就在while(rs.next()) 當rs.next()時內存不斷增大,而不是寫流的問題,JAVA的ResultSet真是麻煩,而且ResultSet還不能clone(); 所以記得在做項目的時候,經常要設置:jdbc.setMaxRows(100*10000); //設置能容納100萬行記錄—–這個就是防止內存泄露的哈——————- 內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;

jdbc.setQueryTimeout(60*30); //設置超時時間是30分鐘

2。java中的內存泄露的情況:長生命周期的對象持有短生命周期對象的引用就很可能發生內存泄露,儘管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導致不能被回收,這就是java中內存泄露的發生場景,通俗地說,就是程序員可能創建了一個對象,以後一直不再使用這個對象,這個對象卻一直被引用,即這個對象無用但是卻無法被垃圾回收器回收的,這就是java中可能出現內存泄露的情況,例如,緩存系統,我們加載了一個對象放在緩存中(例如放在一個全局map對象中),然後一直不再使用它,這個對象一直被緩存引用,但卻不再被使用。

public class Stack { //長生命周期

private Object[] elements=new Object[10]; //當數組容器中沒有東西時是無用的,但是無法回收~~elements是短生命周期

private int size = 0;

public void push(Object e){

ensureCapacity();

elements[size++] = e;

}

public Object pop(){

if( size == 0)

throw new EmptyStackException();

//這裡還是引用了,只是指針變位置變化而已,他確實返回了那個對象,但是他卻沒有斷開那個對象的引用,也就是說有兩個地方會持有這個對象的引用,調用pop的地方,和elements中

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個元素,然後全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內存泄露的兩個條件(必要條件):無用,無法回收。

例子1

public class Bad{

public static Stack s=Stack();

static{

s.push(new Object());

s.pop(); //這裡有一個對象發生內存泄露

s.push(new Object()); //上面的對象可以被回收了,等於是自愈了,因為引用被覆蓋了

}

}

因為是static,就一直存在到程序退出,但是我們也可以看到它有自愈功能,就是說如果你的Stack最多有100個對象,那麼最多也就只有100個對象無法被回收其實這個應該很容易理解,Stack內部持有100個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的進取,以前的引用自然消失!

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

這是屬於: 集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;

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

4。啟動參數內存值設定的過小;

常見的內存泄漏原因及解決方法

(Memory Leak,內存泄漏)

當一個對象已經不需要再使用本該被回收時,另外一個正在使用的對象持有它的引用從而導致它不能被回收,這導致本該被回收的對象不能被回收而停留在堆內存中,這就產生了內存泄漏。

內存泄漏是造成應用程序OOM的主要原因之一。我們知道Android系統為每個應用程序分配的內存是有限的,而當一個應用中產生的內存泄漏比較多時,這就難免會導致應用所需要的內存超過系統分配的內存限額,這就造成了內存溢出從而導致應用Crash。

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

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

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

由於單例的靜態特性使得其生命周期和應用的生命周期一樣長,如果一個對象已經不再需要使用了,而單例對象還持有該對象的引用,就會使得該對象不能被正常回收,從而導致了內存泄漏。

示例:防止單例導致內存泄漏的實例

這樣不管傳入什麼Context最終將使用Application的Context,而單例的生命周期和應用的一樣長,這樣就防止了內存泄漏。???

例如,有時候我們可能會在啟動頻繁的Activity中,為了避免重複創建相同的數據資源,可能會出現如下寫法:

這樣在Activity內部創建了一個非靜態內部類的單例,每次啟動Activity時都會使用該單例的數據。雖然這樣避免了資源的重複創建,但是這種寫法卻會造成內存泄漏。因為非靜態內部類默認會持有外部類的引用,而該非靜態內部類又創建了一個靜態的實例,該實例的生命周期和應用的一樣長,這就導致了該靜態實例一直會持有該Activity的引用,從而導致Activity的內存資源不能被正常回收。

解決方法 :將該內部類設為靜態內部類或將該內部類抽取出來封裝成一個單例,如果需要使用Context,就使用Application的Context。

示例:創建匿名內部類的靜態對象

1、從Android的角度

當Android應用程序啟動時,該應用程序的主線程會自動創建一個Looper對象和與之關聯的MessageQueue。當主線程中實例化一個Handler對象後,它就會自動與主線程Looper的MessageQueue關聯起來。所有發送到MessageQueue的Messag都會持有Handler的引用,所以Looper會據此回調Handle的handleMessage()方法來處理消息。只要MessageQueue中有未處理的Message,Looper就會不斷的從中取出並交給Handler處理。另外,主線程的Looper對象會伴隨該應用程序的整個生命周期。

2、 Java角度

在Java中,非靜態內部類和匿名類內部類都會潛在持有它們所屬的外部類的引用,但是靜態內部類卻不會。

對上述的示例進行分析,當MainActivity結束時,未處理的消息持有handler的引用,而handler又持有它所屬的外部類也就是MainActivity的引用。這條引用關係會一直保持直到消息得到處理,這樣阻止了MainActivity被垃圾回收器回收,從而造成了內存泄漏。

解決方法 :將Handler類獨立出來或者使用靜態內部類,這樣便可以避免內存泄漏。

示例:AsyncTask和Runnable

AsyncTask和Runnable都使用了匿名內部類,那麼它們將持有其所在Activity的隱式引用。如果任務在Activity銷毀之前還未完成,那麼將導致Activity的內存資源無法被回收,從而造成內存泄漏。

解決方法 :將AsyncTask和Runnable類獨立出來或者使用靜態內部類,這樣便可以避免內存泄漏。

對於使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等資源,應該在Activity銷毀時及時關閉或者註銷,否則這些資源將不會被回收,從而造成內存泄漏。

1)比如在Activity中register了一個BraodcastReceiver,但在Activity結束後沒有unregister該BraodcastReceiver。

2)資源性對象比如Cursor,Stream、File文件等往往都用了一些緩衝,我們在不使用的時候,應該及時關閉它們,以便它們的緩衝及時回收內存。它們的緩衝不僅存在於 java虛擬機內,還存在於java虛擬機外。如果我們僅僅是把它的引用設置為null,而不關閉它們,往往會造成內存泄漏。

3)對於資源性對象在不使用的時候,應該調用它的close()函數將其關閉掉,然後再設置為null。在我們的程序退出時一定要確保我們的資源性對象已經關閉。

4)Bitmap對象不在使用時調用recycle()釋放內存。2.3以後的bitmap應該是不需要手動recycle了,內存已經在java層了。

初始時ListView會從BaseAdapter中根據當前的屏幕布局實例化一定數量的View對象,同時ListView會將這些View對象緩存起來。當向上滾動ListView時,原先位於最上面的Item的View對象會被回收,然後被用來構造新出現在下面的Item。這個構造過程就是由getView()方法完成的,getView()的第二個形參convertView就是被緩存起來的Item的View對象(初始化時緩存中沒有View對象則convertView是null)。

構造Adapter時,沒有使用緩存的convertView。

解決方法 :在構造Adapter時,使用緩存的convertView。

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

解決方法 :在退出程序之前,將集合里的東西clear,然後置為null,再退出程序。

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

解決方法 :為WebView另外開啟一個進程,通過AIDL與主線程進行通信,WebView所在的進程可以根據業務的需要選擇合適的時機進行銷毀,從而達到內存的完整釋放。

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)靜態內部類中使用弱引用來引用外部類的成員變量

Android內存泄漏總結

java內存溢出是什麼情況?

首先先說一下JVM內存結構問題,JVM為兩塊:PermanentSapce和HeapSpace,其中\x0d\x0aHeap = }。PermantSpace負責保存反射對象,一般不用配置。JVM的Heap區可以通過-X參數來設定。\x0d\x0a 當一個URL被訪問時,內存申請過程如下:\x0d\x0aA. JVM會試圖為相關Java對象在Eden中初始化一塊內存區域\x0d\x0aB. 當Eden空間足夠時,內存申請結束。否則到下一步\x0d\x0aC. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收), 釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區\x0d\x0aD. Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區\x0d\x0aE. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)\x0d\x0aF. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden複製過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現」out of memory錯誤」\x0d\x0a\x0d\x0aJVM調優建議:\x0d\x0a\x0d\x0ams/mx:定義YOUNG+OLD段的總尺寸,ms為JVM啟動時YOUNG+OLD的內存大小;mx為最大可佔用的YOUNG+OLD內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。\x0d\x0aNewSize/MaxNewSize:定義YOUNG段的尺寸,NewSize為JVM啟動時YOUNG的內存大小;MaxNewSize為最大可佔用的YOUNG內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。\x0d\x0aPermSize/MaxPermSize:定義Perm段的尺寸,PermSize為JVM啟動時Perm的內存大小;MaxPermSize為最大可佔用的Perm內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。\x0d\x0aSurvivorRatio:設置Survivor空間和Eden空間的比例\x0d\x0a\x0d\x0a內存溢出的可能性\x0d\x0a\x0d\x0a1. OLD段溢出\x0d\x0a這種內存溢出是最常見的情況之一,產生的原因可能是:\x0d\x0a1) 設置的內存參數過小(ms/mx, NewSize/MaxNewSize)\x0d\x0a2) 程序問題\x0d\x0a單個程序持續進行消耗內存的處理,如循環幾千次的字符串處理,對字符串處理應建議使用StringBuffer。此時不會報內存溢出錯,卻會使系統持續垃圾收集,無法處理其它請求,相關問題程序可通過Thread Dump獲取(見系統問題診斷一章)單個程序所申請內存過大,有的程序會申請幾十乃至幾百兆內存,此時JVM也會因無法申請到資源而出現內存溢出,對此首先要找到相關功能,然後交予程序員修改,要找到相關程序,必須在Apache日誌中尋找。\x0d\x0a當Java對象使用完畢後,其所引用的對象卻沒有銷毀,使得JVM認為他還是活躍的對象而不進行回收,這樣累計佔用了大量內存而無法釋放。由於目前市面上還沒有對系統影響小的內存分析工具,故此時只能和程序員一起定位。\x0d\x0a\x0d\x0a2. Perm段溢出\x0d\x0a通常由於Perm段裝載了大量的Servlet類而導致溢出,目前的解決辦法:\x0d\x0a1) 將PermSize擴大,一般256M能夠滿足要求\x0d\x0a2) 若別無選擇,則只能將servlet的路徑加到CLASSPATH中,但一般不建議這麼處理\x0d\x0a\x0d\x0a3. C Heap溢出\x0d\x0a系統對C Heap沒有限制,故C Heap發生問題時,Java進程所佔內存會持續增長,直到佔用所有可用系統內存\x0d\x0a\x0d\x0a參數說明:\x0d\x0a\x0d\x0aJVM 堆內存(heap)設置選項 \x0d\x0a 參數格式 \x0d\x0a 說 明 \x0d\x0a \x0d\x0a設置新對象生產堆內存(Setting the Newgeneration heap size) \x0d\x0a -XX:NewSize \x0d\x0a 通過這個選項可以設置Java新對象生產堆內存。在通常情況下這個選項的數值為1 024的整數倍並且大於1MB。這個值的取值規則為,一般情況下這個值-XX:NewSize是最大堆內存(maximum heap size)的四分之一。增加這個選項值的大小是為了增大較大數量的短生命周期對象 \x0d\x0a\x0d\x0a增加Java新對象生產堆內存相當於增加了處理器的數目。並且可以並行地分配內存,但是請注意內存的垃圾回收卻是不可以並行處理的 \x0d\x0a \x0d\x0a設置最大新對象生產堆內存(Setting the maximum New generation heap size) \x0d\x0a -XX:MaxNewSize \x0d\x0a 通過這個選項可以設置最大Java新對象生產堆內存。通常情況下這個選項的數值為1 024的整數倍並且大於1MB \x0d\x0a\x0d\x0a其功用與上面的設置新對象生產堆內存-XX:NewSize相同\x0d\x0a\x0d\x0a設置新對象生產堆內存的比例(Setting New heap size ratios) \x0d\x0a -XX:SurvivorRatio \x0d\x0a 新對象生產區域通常情況下被分為3個子區域:伊甸園,與兩個殘存對象空間,這兩個空間的大小是相同的。通過用-XX:SurvivorRatio=X選項配置伊甸園與殘存對象空間(Eden/survivor)的大小的比例。你可以試着將這個值設置為8,然後監控、觀察垃圾回收的工作情況\x0d\x0a\x0d\x0a設置堆內存池的最大值(Setting maximum heap size) \x0d\x0a -Xmx \x0d\x0a 通過這個選項可以要求系統為堆內存池分配內存空間的最大值。通常情況下這個選項的數值為1 024的整數倍並且大於1 MB \x0d\x0a\x0d\x0a一般情況下這個值(-Xmx)與最小堆內存(minimum heap size _Xms)相同,以降低垃圾回收的頻度 \x0d\x0a \x0d\x0a取消垃圾回收 \x0d\x0a -Xnoclassgc \x0d\x0a 這個選項用來取消系統對特定類的垃圾回收。它可以防止當這個類的所有引用丟失之後,這個類仍被引用時不會再一次被重新裝載,因此這個選項將增大系統堆內存的空間 \x0d\x0a \x0d\x0a設置棧內存的大小 \x0d\x0a -Xss \x0d\x0a 這個選項用來控制本地線程棧的大小,當這個選項被設置的較大(2MB)時將會在很大程度上降低系統的性能。因此在設置這個值時應該格外小心,調整後要注意觀察系統的性能,不斷調整以期達到最優 \x0d\x0a \x0d\x0a最後說一句,你的機器的連接數設置也至關重要,連接的關閉最好把時間設置的少些,那些連接非常耗費資源。也是引起內存泄露的主要原因。

原創文章,作者:UEMD,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/145237.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
UEMD的頭像UEMD
上一篇 2024-10-27 23:47
下一篇 2024-10-27 23:47

相關推薦

  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean加載過程

    Java Bean加載過程涉及到類加載器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean加載的過程。 一、類加載器 類加載器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Python創建分配內存的方法

    在python中,我們常常需要創建並分配內存來存儲數據。不同的類型和數據結構可能需要不同的方法來分配內存。本文將從多個方面介紹Python創建分配內存的方法,包括列表、元組、字典、…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發佈。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字符串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字符串中是否存在多個指定字符: 一、字符串遍歷 字符串是Java編程中非常重要的一種數據類型。要判斷字符串中是否存在多個指定字符…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29

發表回復

登錄後才能評論