① 如何用java編寫一段代碼引發內存泄露
1、首先得搞清楚什麼叫內存泄露,簡單來說就是一個東西放塌液在內存里的時間太長了,當你的程序都跑完了,它還存在那裡。這時它是白白的佔用了你的內存,累積起來佔用的內存越來越多……最後就會導致JVM報錯:out of memory。
2、一般情況下,別人如果能指出你的系統(程序)內存溢出,這個人應該還是挺厲害槐滲的。通常對於新人來說,喜歡把變數直接定義在class下(此時稱之為實例變數,或者成員變數),那麼在方法里調用後,這個實例變數是不會被釋放的,大量的這樣使用就可能會引發內存泄露。
3、把變數定義在方法里,當這個方法執行完畢後內存就得到釋放了,這是個好習慣。
4、如果想要看到內存溢出,可以按這樣的思路去嘗試一下:定義一個靜態的實例變數(list或其它集合),然後在一個方法里循環往這個靜態變數塞東西,直到這個實例變數撐爆你的jvm內存。很團明物快你就能看到out of memory……
import java.util.ArrayList;
import java.util.List;
public class MemoryTest {
private static List list = new ArrayList();
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
System.out.println("申請前的可用內存 = "+getFreeMemory());
while(true){
list.add(new byte[1024*1024]);//用實例變數申請1M內存,當方法執行完畢時,這個static的變數是不會被釋放
count++;
if (count % 100 == 0) {
System.out.println("當前list.size()="+list.size()+",可用內存 = "+getFreeMemory());
Thread.sleep(500);
}
}
}
public static long getFreeMemory() {
return Runtime.getRuntime().freeMemory() / (1024 * 1024);
}
}
② java內存泄漏怎麼處理
一、Java內存回收機制
不論哪種語言的內存分配方式,都需要返回所分配內存的真實地址,也就是返回一個指針到內存塊的首地址。Java中對象是採用new或者反射的方法創建的,這些對象的創建都是在堆(Heap)中分配的,所有對象的回收都是由Java虛羨逗擬機通過垃圾回收機制完成的。GC為了能夠正確釋放對象,會監控每個對象的運行狀況,對他們的申請、引用、被引用、賦值等狀況進行監控,Java會使用有向圖的方法進行管理內存,實時監控對象是否可以達到,如果不可到達,則就將其回收,這樣也可以消除引用循環的問題。在Java語言中,判斷一個內存空間是否符合垃圾收集標准有兩個:一個是給對象賦予了空值null,以下再沒有調用過,另一個是給對象賦予了新值,這樣重新分配了內存空間。
二、Java內存泄露引起原因
首先,什麼是內存泄露看經常聽人談起內存泄露,但要問什麼是內存泄露,沒幾個說得清楚。內存泄露是指無用對象(不再使用的對象)持續佔有內存或無用對象的內存得不到及時釋放,從而造成的內存空間的浪費稱為內存泄露。內存泄露有時不嚴重且不易察覺,這樣開發者就不知道存在內存泄兄散賣露,但有時也會很嚴重,會提示你Out of memory。
那麼,Java內存泄露根本原因是什麼呢看長生命周期的對象持有短生命周期對象的引用就很可能發生內存泄露,盡管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導致不能被回收,這就是java中內存泄露的發生場景。具體主要有如下幾大類:
1、靜態集合類引起內存泄露:
像HashMap、Vector等的使用最容易出現內存泄露,這些靜態變數的生命周期和應用程序一致,他們所引用的所有的掘攔對象Object也不能被釋放,因為他們也將一直被Vector等引用著。
例:
Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}//
在這個例子中,循環申請Object 對象,並將所申請的對象放入一個Vector 中,如果僅僅釋放引用本身(o=null),那麼Vector 仍然引用該對象,所以這個對象對GC 來說是不可回收的。因此,如果對象加入到Vector 後,還必須從Vector 中刪除,最簡單的方法就是將Vector對象設置為null。
2、當集合裡面的對象屬性被修改後,再調用remove()方法時不起作用。
例:
public static void main(String[] args)
{
Set set = new HashSet();
Person p1 = new Person("唐僧","pwd1",25);
Person p2 = new Person("孫悟空","pwd2",26);
Person p3 = new Person("豬八戒","pwd3",27);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println("總共有:"+set.size()+" 個元素!"); //結果:總共有:3 個元素!
p3.setAge(2); //修改p3的年齡,此時p3元素對應的hashcode值發生改變
set.remove(p3); //此時remove不掉,造成內存泄漏
set.add(p3); //重新添加,居然添加成功
System.out.println("總共有:"+set.size()+" 個元素!"); //結果:總共有:4 個元素!
for (Person person : set)
{
System.out.println(person);
}
}
3、監聽器
在java 編程中,我們都需要和監聽器打交道,通常一個應用當中會用到很多監聽器,我們會調用一個控制項的諸如addXXXListener()等方法來增加監聽器,但往往在釋放對象的時候卻沒有記住去刪除這些監聽器,從而增加了內存泄漏的機會。
4、各種連接
比如資料庫連接(dataSourse.getConnection()),網路連接(socket)和io連接,除非其顯式的調用了其close()方法將其連接關閉,否則是不會自動被GC 回收的。對於Resultset 和Statement 對象可以不進行顯式回收,但Connection 一定要顯式回收,因為Connection 在任何時候都無法自動回收,而Connection一旦回收,Resultset 和Statement 對象就會立即為NULL。但是如果使用連接池,情況就不一樣了,除了要顯式地關閉連接,還必須顯式地關閉Resultset Statement 對象(關閉其中一個,另外一個也會關閉),否則就會造成大量的Statement 對象無法釋放,從而引起內存泄漏。這種情況下一般都會在try裡面去的連接,在finally裡面釋放連接。
5、內部類和外部模塊等的引用
內部類的引用是比較容易遺忘的一種,而且一旦沒釋放可能導致一系列的後繼類對象沒有釋放。此外程序員還要小心外部模塊不經意的引用,例如程序員A 負責A 模塊,調用了B 模塊的一個方法如:
public void registerMsg(Object b);
這種調用就要非常小心了,傳入了一個對象,很可能模塊B就保持了對該對象的引用,這時候就需要注意模塊B 是否提供相應的操作去除引用。
6、單例模式
不正確使用單例模式是引起內存泄露的一個常見問題,單例對象在被初始化後將在JVM的整個生命周期中存在(以靜態變數的方式),如果單例對象持有外部對象的引用,那麼這個外部對象將不能被jvm正常回收,導致內存泄露,考慮下面的例子:
class A{
public A(){
B.getInstance().setA(this);
}
....
}
//B類採用單例模式
class B{
private A a;
private static B instance=new B();
public B(){}
public static B getInstance(){
return instance;
}
public void setA(A a){
this.a=a;
}
//getter...
}
顯然B採用singleton模式,它持有一個A對象的引用,而這個A類的對象將不能被回收。想像下如果A是個比較復雜的對象或者集合類型會發生什麼情況
③ java中是否會存在內存泄漏
會。java導致內存泄露的原因很明確:長生命周期的對象持有短生命周期對象的引用就很可能發生內存泄露,盡管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導致不能被回收,這就是java中內存泄露的發生場景。
1.集合類,集合類僅僅有添加元素的方法,而弊返察沒有相應的刪除機制,導致內存被佔用。這一點其實也不明確,這個集合類如果僅僅是局部變數,根本不會造成內存泄露,在方法棧退出後就沒有引用了會被jvm正常回收。而如果這個集合類是全局性的變數(比如類中的靜態屬性,全局性的map等即有靜態引用或final一直指向它),那麼沒有相應的刪除機制,很可能導致集合所佔用的內存只增不減,因此提供這樣的刪除機制或者定期清除策略非常必要。
2.單例模式。不正確使用單例模式是引起內存泄露的一個常見問題,單例對象在被初始化後將在JVM的整個生命周期中存在(以靜態變數的方式),如果單例對象持有外部對世宴象的引用,那麼這個外部對象將不能租茄被jvm正常回收,導致內存泄露,考慮下面的例子:class A{
public A(){
B.getInstance().setA(this);}
....}
//B類採用單例模式class B{
private A a;
private static B instance=new B();
public B(){}
public static B getInstance(){
return instance;}
④ Android技術分享|Android 中部分內存泄漏示例及解決方案
內存泄漏:
舉例:
請注意以下的例子是虛構的
內存抖動
源自Android文檔中的 Memory churn 一詞,中文翻譯為內存抖動。
指快速頻繁的創建對象從而產生的性能問題。
引用Android文檔原文:
Java內存泄漏的根本原因是 長生命周期 的對象持有 短生命周期 對象的引用就很可能發生內存泄漏。
盡管短生命周期對象已經不再需要,但因為長生命周期依舊持有它的引用,故不能被回收而導致內存泄漏。
靜態集合類引起的內存泄漏
如果僅僅釋放引用本身(tO = null), ArrayList 依然在引用該對象,GC無法回收。
監聽器
在Java應用中,通常會用到很多監聽器,一般通過 addXXXXListener() 實現。但釋放對象時通常會忘記刪除監聽器,從而增加內存泄漏的風險。
各種連接
如資料庫連接、網路連接(Socket)和I/O連接。忘記顯式調用 close() 方法引起的內存泄漏。
內部類和外部模塊的引用
內部類的引用是很容易被遺忘的一種,一旦沒有釋放可能會導致一系列後續對象無法釋放。此外還要小心外部模塊不經意的引用,內部類是否提供相應的操作去除外部引用。
單例模式
由於單例的靜態特性,使其生命周期與應用的生命周期一樣長,一旦使用不恰當極易造成內存泄漏。如果單利持有外部引用,需要注意提供釋放方式,否則當外部對象無法被正常回收時,會進而導致內存泄漏。
集合類泄漏
如集合的使用范圍超過邏輯代碼的范圍,需要格外注意刪除機制是否完善可靠。比如由靜態屬性 static 指向的集合。
單利泄漏
以下為簡單邏輯代碼,只為舉例說明內存泄漏問題,不保證單利模式的可靠性。
AppManager 創建時需要傳入一個 Context ,這個 Context 的生命周期長短至關重要。
1. 如果傳入的是 Application 的 Context ,因為 Application 的生命周期等同於應用的生命周期,所以沒有任何問題。
2. 如果傳入的是 Activity 的 Context ,則需要考慮這個 Activity 是否在整個生命周期都不會被回收了,如果不是,則會造成內存泄漏。
非靜態內部類創建靜態實例造成的內存泄漏
應該將該內部類單獨封裝為一個單例來使用。
匿名內部類/非同步線程
Runnable都使用了匿名內部類,將持有MyActivity的引用。如果任務在Activity銷毀前未完成,將導致Activity的內存無法被回收,從而造成內存泄漏。
解決方法:將Runnable獨立出來或使用靜態內部類,可以避免因持有外部對象導致的內存泄漏。
Handler造成的內存泄漏
Handler屬於TLS(Thread Local Storage)變數,生命周期與Activity是不一致的,容易導致持有的對象無法正確被釋放
當Android應用程序啟動時,該應用程序的主線程會自動創建一個Looper對象和與之關聯的MessageQueue。
當主線程中實例化一個Handler對象後,它就會自動與主線程Looper的MessageQueue關聯起來。所有發送到MessageQueue的Messag都會持有Handler的引用,所以Looper會據此回調Handle的handleMessage()方法來處理消息。只要MessageQueue中有未處理的Message,Looper就會不斷的從中取出並交給Handler處理。
另外,主線程的Looper對象會伴隨該應用程序的整個生命周期。
在Java中,非靜態內部類和匿名類內部類都會潛在持有它們所屬的外部類的引用,但是靜態內部類卻不會。
當該 Activity 被 finish() 掉時,延遲執行任務的 Message 還會繼續存在於主線程中,它持有該 Activity 的 Handler 引用,所以此時 finish() 掉的 Activity 就不會被回收了從而造成內存泄漏(因 Handler 為非靜態內部類,它會持有外部類的引用,在這里就是指 SampleActivity)。
避免不必要的靜態成員變數
對於BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等資源的使用,應在Activity銷毀前及時關閉或注銷。
不使用WebView對象時,應調用`destroy()`方法銷毀。
⑤ 如何識別Java中的內存泄漏
JAVA有局帶侍GC,所以很少會發生內存桐吵泄漏的情況。
主要看程序運行過程中內存的使用量是行伏否隨著時間的增加而增加。
⑥ java內存泄漏怎麼解決
一般情況下內存泄漏的避免
在不涉及復雜數據結構茄哪的一般情況下,Java 的內存泄露表現為一個內存對象的生命周期超出了程序需要它的時間長度。我們有時也將其稱為「對象游離」。
例如:
public class FileSearch{ private byte [] content; private File mFile; public FileSearch(File file){ mFile = file; } public boolean hasString(String str){ int size = getFileSize(mFile); content = new byte [size]; loadFile(mFile, content); String s = new String(content); return s.contains(str); }}
在這段代碼中,FileSearch 類中有一個函數 hasString ,用來判斷文檔中是否含有指定的字元串。流程是先將mFile 載入到內存中,然後進行判斷。但是,這里的問題是,將 content 聲明為了實例變數,而不是本地變數。於是,在此函數返回之後,內存中仍然存在整個文件的數據。而很明顯,這些數據我們後續是不再需要的,這就造成了內存的無故浪費。
要避免這種情況下的內存泄露,要求我們以C/C++ 的內存管理思維來管理自己分配的內存。第一,是在聲明對象引用之前,明確內存對象的有效作用域。在一個函數內有效的內存對象,應該聲明為 local 變數,與類實例生命周期相同的要聲明為實例變數……以此類推。第二,在內存對象不再需要時,記得手動將其引用置空。
復雜數據結構中的內存泄露問題
在實際的項目中,我們經常用到一些較為復雜的數據結構用於緩存程序運行過程中需要的數據信息。有時,由於數據結構過於復雜,或者我們存在一些特殊的需求(例如,在內存允許的情況下,盡可能多的緩存信息來提高程序的運行速度等情況),我們很難對數據結構中數據的生命周期作出明確的界定。這個時候,我們可以使用Java 中一種特殊的機制來達到防止內存泄露的目的。
之前我們介紹過,Java 的 GC 機制是建立在跟蹤內存的引用機制上的。而在此之前,我們所使用的引用都只是定義一個「 Object o; 」這樣形式的。事實上,這只是 Java 引用機制中的一種默認情況,除此之外,還有其他的一些引用方式。通過使用這些特殊的引用機制,配合 GC 機制,就可以達到一些我改仔們需要的效果。
Java中的幾種引用方式
Java中有幾種不同的引用方式,它們分別是:強引用、軟引用、弱引用和虛引用。下面,我們首先詳細地了解下這幾種引用方式的意義。
強引用
在此之前我們介紹的內容中所使用的引用 都是強引用,這是使用最普遍的引用。如果一個對象具有強引用,那就類似於必不可少的生活用品,垃圾回收器絕不會回收它。當內存空 間不足,Java 虛擬機寧願拋出 OutOfMemoryError 錯誤,使程序異常終止,也不會靠隨核納汪意回收具有強引用的對象來解決內存不足問題。
軟引用(SoftReference )
SoftReference 類的一個典型用途就是用於內存敏感的高速緩存。SoftReference 的原理是:在保持對對象的引用時保證在 JVM 報告內存不足情況之前將清除所有的軟引用。關鍵之處在於,垃圾收集器在運行時可能會(也可能不會)釋放軟可及對象。對象是否被釋放取決於垃圾收集器的演算法 以及垃圾收集器運行時可用的內存數量。
弱引用(WeakReference )
WeakReference 類的一個典型用途就是規范化映射( canonicalized mapping )。另外,對於那些生存期相對較長而且重新創建的開銷也不高的對象來說,弱引用也比較有用。關鍵之處在於,垃圾收集器運行時如果碰到了弱可及對象,將釋放 WeakReference 引用的對象。然而,請注意,垃圾收集器可能要運行多次才能找到並釋放弱可及對象。
虛引用(PhantomReference )
PhantomReference 類只能用於跟蹤對被引用對象即將進行的收集。同樣,它還能用於執行 pre-mortem 清除操作。PhantomReference 必須與 ReferenceQueue 類一起使用。需要 ReferenceQueue 是因為它能夠充當通知機制。當垃圾收集器確定了某個對象是虛可及對象時, PhantomReference 對象就被放在它的 ReferenceQueue 上。將 PhantomReference 對象放在 ReferenceQueue 上也就是一個通知,表明 PhantomReference 對象引用的對象已經結束,可供收集了。這使您能夠剛好在對象佔用的內存被回收之前採取行動。Reference與 ReferenceQueue 的配合使用。
GC、 Reference 與 ReferenceQueue 的交互
A、 GC無法刪除存在強引用的對象的內存。
B、 GC發現一個只有軟引用的對象內存,那麼:
① SoftReference對象的 referent 域被設置為 null ,從而使該對象不再引用 heap 對象。
② SoftReference引用過的 heap 對象被聲明為 finalizable 。
③ 當 heap 對象的 finalize() 方法被運行而且該對象佔用的內存被釋放, SoftReference 對象就被添加到它的 ReferenceQueue (如果後者存在的話)。
C、 GC發現一個只有弱引用的對象內存,那麼:
① WeakReference對象的 referent 域被設置為 null , 從而使該對象不再引用heap 對象。
② WeakReference引用過的 heap 對象被聲明為 finalizable 。
③ 當heap 對象的 finalize() 方法被運行而且該對象佔用的內存被釋放時, WeakReference 對象就被添加到它的 ReferenceQueue (如果後者存在的話)。
D、 GC發現一個只有虛引用的對象內存,那麼:
① PhantomReference引用過的 heap 對象被聲明為 finalizable 。
② PhantomReference在堆對象被釋放之前就被添加到它的 ReferenceQueue 。
值得注意的地方有以下幾點:
1、 GC 在一般情況下不會發現軟引用的內存對象,只有在內存明顯不足的時候才會發現並釋放軟引用對象的內存。
2、 GC 對弱引用的發現和釋放也不是立即的,有時需要重復幾次 GC ,才會發現並釋放弱引用的內存對象。3、軟引用和弱引用在添加到 ReferenceQueue 的時候,其指向真實內存的引用已經被置為空了,相關的內存也已經被釋放掉了。而虛引用在添加到 ReferenceQueue 的時候,內存還沒有釋放,仍然可以對其進行訪問。
代碼示例
通過以上的介紹,相信您對Java 的引用機制以及幾種引用方式的異同已經有了一定了解。光是概念,可能過於抽象,下面我們通過一個例子來演示如何在代碼中使用 Reference 機制。
String str = new String( " hello " ); // ①ReferenceQueue < String > rq = new ReferenceQueue < String > (); // ②WeakReference < String > wf = new WeakReference < String > (str, rq); // ③str = null ; // ④取消"hello"對象的強引用String str1 = wf.get(); // ⑤假如"hello"對象沒有被回收,str1引用"hello"對象// 假如"hello"對象沒有被回收,rq.poll()返回nullReference <? extends String > ref = rq.poll(); // ⑥
在以上代碼中,注意⑤⑥兩處地方。假如「hello 」對象沒有被回收 wf.get() 將返回「 hello 」字元串對象, rq.poll() 返回 null ;而加入「 hello 」對象已經被回收了,那麼 wf.get() 返回 null , rq.poll() 返回 Reference 對象,但是此 Reference 對象中已經沒有 str 對象的引用了 ( PhantomReference 則與WeakReference 、 SoftReference 不同 )。
引用機制與復雜數據結構的聯合應用
了解了GC 機制、引用機制,並配合上 ReferenceQueue ,我們就可以實現一些防止內存溢出的復雜數據類型。
例如,SoftReference 具有構建 Cache 系統的特質,因此我們可以結合哈希表實現一個簡單的緩存系統。這樣既能保證能夠盡可能多的緩存信息,又可以保證 Java 虛擬機不會因為內存泄露而拋出 OutOfMemoryError 。這種緩存機制特別適合於內存對象生命周期長,且生成內存對象的耗時比較長的情況,例如緩存列表封面圖片等。對於一些生命周期較長,但是生成內存對象開銷不大的情況,使用WeakReference 能夠達到更好的內存管理的效果。
附SoftHashmap 的源碼一份,相信看過之後,大家會對 Reference 機制的應用有更深入的理解。
⑦ 如何編寫一個java內存泄露程序
自己改一下下面的代碼,把堆棧中的元素改成mp3類型的或更大點的東西
4.Java中參數都是傳值的。
對於基本類型,大家基本上沒有異議,但是對於引用類型我們也不能有異議。
Java內存泄露情況
JVM回收演算法 是很復雜的,我也不知道他們怎麼實現的,但是我只知道他們要實現的就是:對於沒有被引用的對象是可以回收的。所以你要造成內存泄露就要做到:
持有對無用對象的引用!
不要以為這個很輕易做到,既然無用,你怎麼還會持有它的引用? 既然你還持有它,它怎麼會是無用的呢?
以下以堆棧更經典這個經典的例子來剖析。
Java代碼
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.array(oldElements,0, elements, 0, size);
}
}
}
上面的原理應該很簡單,假如堆棧加了10個元素,然後全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內存泄露的兩個條件:無用,無法回收。
但是就是存在這樣的東西也不一定會導致什麼樣的後果,假如這個堆棧用的比較少,也就浪費了幾個K內存而已,反正我們的內存都上G了,哪裡會有什麼影響,再說這個東西很快就會被回收的,有什麼關系。下面看兩個例子。
例子1
Java代碼
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個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的銀氏遲進取,以前的引用自然消失!
例子2
Java代碼
public class NotTooBad{
public void doSomething(){
Stack s=new Stack();
s.push(new Object());
//other code
s.pop();//這里同樣導致對象無法回收,內存泄露.
}//退出方法,s自動無效,s可以被回收,Stack內部的引用自然沒了,所以
//這里也可以自愈,而且可以說這個方法不存在內存泄露問題,不過是晚一點
//交給GC而已,因為它是封閉的,對外不開放,可以說上面的代碼核告99.9999%的
//情況是不會造成任何影響的,當然你寫這樣的代碼不會有什麼壞的影響,但是
//絕對可以說是垃圾代碼!沒有矛盾吧,我在裡面加一個空的for循環也不會有
//什麼鋒李太大的影響吧,你會這么做嗎?
}
⑧ java內存泄露是什麼意思
Java內存泄露x0dx0a一般來說內存泄漏有兩種情況。一種情況如在C/C++語言中的,在堆中的分配的內存,在沒有將其釋放掉的時候,就將所有能訪問這塊內存的方式都刪掉(如指針重新賦值);另一種情況則是在內存對象明明已經不需要的時候,還仍然保留著這塊內存和它的訪問方納悄式(引用)。第一種情況,在Java中已經由於垃圾回收機制的引入,得到了很好的解決。所以,Java中的內存泄漏,主要指的是第二種情況。x0dx0a可能光說概念太抽象了,大家可以看一下這樣的例子:x0dx0a1Vectorv=newVector(10);x0dx0a2for(inti=1;i<100;i++){x0dx0a3Objecto=newObject();x0dx0a4v.add(o);x0dx0a5o=null;x0dx0a6}x0dx0a在這個例子中,代碼棧中存在Vector對象的引用v和Object對象的引用o。在For循環中,不斷的生成新的對象,然後將其添加到Vector對象中,之後將o引用置空。問題是當o引用被置空後,如果發生GC,創建的Object對象是否能夠被GC回收呢?答案是否定的。因為,GC在跟蹤代碼棧中的引用時,會發現v引用,而繼續往下跟蹤,就會發現v引用指向的內存空間中又存在指向Object對象的引用。也就是說盡管o引用已經被置空,但是Object對象仍然存在其他的引用,是可以被訪問到的,所以GC無法將其釋放掉。如果在此循環之後,Object對象對程序已經沒有任何作用,那麼就認為此Java程序發生了內存泄漏。x0dx0a盡管對於C/C++中的內存泄露情況來說,Java內存泄露導致的破壞性小,除了少數情況會出現程序崩潰的情況外,大多數情況下程序仍然能正常運行。但是,在移動設備對於內存和CPU都有較嚴格的限制的情況下,Java的內存溢出會導致程序效率低下、佔用大量不需要的內存等問題。這將導致整個機器性能變差,嚴重的也會引起拋出OutOfMemoryError,導致程序崩潰。x0dx0a一般情況下內存泄漏的避免x0dx0a在不涉及復雜數據結構的一般情況下,Java的內存泄露表現為一個內存對象的生命周期超出了程序需要它的時間長度。有時也將其稱為「對象游離」。x0dx0a例如:x0dx0a1publicclassFileSearch{x0dx0a2x0dx0a3privatebyte[]content;x0dx0a4privateFilemFile;(Filefile){x0dx0a7mFile=file;x0dx0a8}(Stringstr){x0dx0a11intsize=getFileSize(mFile);x0dx0a12content=newbyte[size];x0dx0a13loadFile(mFile,content);x0dx0a14x0dx0a15Strings=newString(content);x0dx0a16returns.contains(str);x0dx0a17}x0dx0a18}x0dx0a在這段代碼中,FileSearch類中有一個函數hasString,用來判斷文檔中是否含有指定的字元串。流程是先將mFile載入到內存中,然後進行判斷。但是,這里瞎猛的問題是,將content聲明為了實例變數,而不是本地變數。於是,在此函數返回之後,內存中仍然存在整個文件的數據。而很明顯,這些數據後續是不再需要的,這就造成了內存的無故浪費。x0dx0a要避免這種情況下的內存泄露,要求以C/C++的內存管理思維來管理自己分配的內存。第一,是在聲明對象引用之前,明確內存對象的有效作用域。在一個函數內有效的內存對象,應該聲明為local變數,與類實例生命周期相同的要聲明為實例變數??以此類推。第二,在內存對象不再需要時,記得手動將其引用置空。x0dx0a復雜數據結構中的內存泄露問題x0dx0a在實際的項目洞神渣中,經常用到一些較為復雜的數據結構用於緩存程序運行過程中需要的數據信息。有時,由於數據結構過於復雜,或者存在一些特殊的需求(例如,在內存允許的情況下,盡可能多的緩存信息來提高程序的運行速度等情況),很難對數據結構中數據的生命周期作出明確的界定。這個時候,可以使用Java中一種特殊的機制來達到防止內存泄露的目的。x0dx0a之前介紹過,Java的GC機制是建立在跟蹤內存的引用機制上的。而在此之前,所使用的引用都只是定義一個「Objecto;」這樣形式的。事實上,這只是Java引用機制中的一種默認情況,除此之外,還有其他的一些引用方式。通過使用這些特殊的引用機制,配合GC機制,就可以達到一些需要的效果。
⑨ 如何模擬一個Java內存泄露的情景
importjava.util.ArrayList;
importjava.util.List;
/**
*常見的內存泄露:
*1.文件流不關閉
*2.資料庫連接沒有關閉
*3.內存使用過多
*/
publicclassTest{
publicstaticvoidmain(String[]args)throwsInterruptedException{
List<int[]>list=newArrayList<int[]>();
Runtimerun=Runtime.getRuntime();
inti=1;
while(true){
int[]arr=newint[1024*8];
list.add(arr);
if(i++%1000==0渣賣蔽){
System.out.print("最大內存="+run.maxMemory()/1024/1024+"M,");
System.out.print("已分配內存="+run.totalMemory()/1024/1024+"M,");
配碧System.out.print("剩餘空間內存="+run.freeMemory()/1024/1024+"M");
如州System.out.println("最大可用內存="+(run.maxMemory()-run.totalMemory()+run.freeMemory())/1024/1024+"M");
}
}
}
}