Java內(nèi)存泄漏分析和解決

最近正在熟悉Java內(nèi)存泄漏的相關(guān)知識(shí)遍愿,上網(wǎng)查閱了一些資料存淫,在此做個(gè)整理算是對(duì)收獲的一些總結(jié),希望能對(duì)各位有所幫助沼填,有問(wèn)題可以文末留言探討桅咆、補(bǔ)充。

如下是整篇文章的結(jié)構(gòu)坞笙,所需閱讀時(shí)間大約20min

Java當(dāng)中的內(nèi)存泄漏

1. 什么是內(nèi)存泄漏岩饼?

內(nèi)存泄漏:對(duì)象已經(jīng)沒(méi)有被應(yīng)用程序使用荚虚,但是垃圾回收器沒(méi)辦法移除它們,因?yàn)檫€在被引用著籍茧。
在Java中版述,內(nèi)存泄漏就是存在一些被分配的對(duì)象,這些對(duì)象有下面兩個(gè)特點(diǎn)寞冯,首先渴析,這些對(duì)象是可達(dá)的,即在有向圖中吮龄,存在通路可以與其相連俭茧;其次這些對(duì)象是無(wú)用的漓帚,即程序以后不會(huì)再使用這些對(duì)象母债。如果對(duì)象滿足這兩個(gè)條件,這些對(duì)象就可以判定為Java中的內(nèi)存泄漏尝抖,這些對(duì)象不會(huì)被GC所回收毡们,然而它卻占用內(nèi)存。

在C++中昧辽,內(nèi)存泄漏的范圍更大一些衙熔。有些對(duì)象被分配了內(nèi)存空間,然后卻不可達(dá),由于C++中沒(méi)有GC(Garbage Collection垃圾回收),這些內(nèi)存將永遠(yuǎn)收不回來(lái)捻撑。在Java中其掂,這些不可達(dá)的對(duì)象都由GC負(fù)責(zé)回收,因此程序員不需要考慮這部分的內(nèi)存泄露扁耐。

通過(guò)分析暇检,我們得知,對(duì)于C++婉称,程序員需要自己管理邊和頂點(diǎn)块仆,而對(duì)于Java程序員只需要管理邊就可以了(不需要管理頂點(diǎn)的釋放)。通過(guò)這種方式王暗,Java提高了編程的效率悔据。

C++與Java當(dāng)中的內(nèi)存泄漏

因此,通過(guò)以上分析俗壹,我們知道在Java中也有內(nèi)存泄漏科汗,但范圍比C++要小一些。因?yàn)镴ava從語(yǔ)言上保證绷雏,任何對(duì)象都是可達(dá)的头滔,所有的不可達(dá)對(duì)象都由GC管理怖亭。

對(duì)于程序員來(lái)說(shuō),GC基本是透明的坤检,不可見(jiàn)的兴猩。雖然,我們只有幾個(gè)函數(shù)可以訪問(wèn)GC早歇,例如運(yùn)行GC的函數(shù)System.gc()倾芝,但是根據(jù)Java語(yǔ)言規(guī)范定義, 該函數(shù)不保證JVM的垃圾收集器一定會(huì)執(zhí)行箭跳。因?yàn)橹恚煌腏VM實(shí)現(xiàn)者可能使用不同的算法管理GC。通常衅码,GC的線程的優(yōu)先級(jí)別較低拯刁。JVM調(diào)用GC的策略也有很多種,有的是內(nèi)存使用到達(dá)一定程度時(shí)逝段,GC才開(kāi)始工作垛玻,也有定時(shí)執(zhí)行的,有的是平緩執(zhí)行GC奶躯,有的是中斷式執(zhí)行GC帚桩。但通常來(lái)說(shuō),我們不需要關(guān)心這些嘹黔。除非在一些特定的場(chǎng)合账嚎,GC的執(zhí)行影響應(yīng)用程序的性能,例如對(duì)于基于Web的實(shí)時(shí)系統(tǒng)儡蔓,如網(wǎng)絡(luò)游戲等郭蕉,用戶不希望GC突然中斷應(yīng)用程序執(zhí)行而進(jìn)行垃圾回收,那么我們需要調(diào)整GC的參數(shù)喂江,讓GC能夠通過(guò)平緩的方式釋放內(nèi)存召锈,例如將垃圾回收分解為一系列的小步驟執(zhí)行,Sun提供的HotSpot JVM就支持這一特性获询。

下面給出一個(gè) Java 內(nèi)存泄漏的典型例子涨岁,

Vector v = new Vector(10);

for (int i = 0; i < 100; i++) {
    Object o = new Object();
    v.add(o);
    o = null;
}

在這個(gè)例子中,我們循環(huán)申請(qǐng)Object對(duì)象吉嚣,并將所申請(qǐng)的對(duì)象放入一個(gè) Vector 中梢薪,如果我們僅僅釋放引用本身,那么 Vector 仍然引用該對(duì)象尝哆,所以這個(gè)對(duì)象對(duì) GC 來(lái)說(shuō)是不可回收的秉撇。因此,如果對(duì)象加入到Vector 后,還必須從 Vector 中刪除畜疾,最簡(jiǎn)單的方法就是將 Vector 對(duì)象設(shè)置為 null赴邻。

v = null

要想理解這個(gè)定義,我們需要先了解一下對(duì)象在內(nèi)存中的狀態(tài)啡捶。下面的這張圖就解釋了什么是無(wú)用對(duì)象以及什么是未被引用對(duì)象姥敛。

內(nèi)存泄漏示意圖

上面圖中可以看出,里面有被引用對(duì)象未被引用對(duì)象瞎暑。未被引用對(duì)象會(huì)被垃圾回收器回收彤敛,而被引用的對(duì)象卻不會(huì)。未被引用的對(duì)象當(dāng)然是不再被使用的對(duì)象了赌,因?yàn)闆](méi)有對(duì)象再引用它墨榄。然而無(wú)用對(duì)象卻不全是未被引用對(duì)象。其中還有被引用的勿她。就是這種情況導(dǎo)致了內(nèi)存泄漏袄秩。

2. 詳細(xì)Java中的內(nèi)存泄漏

2.1 Java內(nèi)存回收機(jī)制

不論哪種語(yǔ)言的內(nèi)存分配方式,都需要返回所分配內(nèi)存的真實(shí)地址逢并,也就是返回一個(gè)指針到內(nèi)存塊的首地址之剧。Java中對(duì)象是采用new或者反射的方法創(chuàng)建的,這些對(duì)象的創(chuàng)建都是在堆(Heap)中分配的砍聊,所有對(duì)象的回收都是由Java虛擬機(jī)通過(guò)垃圾回收機(jī)制完成的背稼。GC為了能夠正確釋放對(duì)象,會(huì)監(jiān)控每個(gè)對(duì)象的運(yùn)行狀況玻蝌,對(duì)他們的申請(qǐng)蟹肘、引用、被引用俯树、賦值等狀況進(jìn)行監(jiān)控帘腹,Java會(huì)使用有向圖的方法進(jìn)行管理內(nèi)存,實(shí)時(shí)監(jiān)控對(duì)象是否可以達(dá)到聘萨,如果不可到達(dá)竹椒,則就將其回收,這樣也可以消除引用循環(huán)的問(wèn)題米辐。在Java語(yǔ)言中,判斷一個(gè)內(nèi)存空間是否符合垃圾收集的標(biāo)準(zhǔn)有兩個(gè):一個(gè)是給對(duì)象賦予了空值null书释,以下再?zèng)]有調(diào)用過(guò)另一個(gè)是給對(duì)象賦予了新值翘贮,這樣重新分配了內(nèi)存空間

2.2 Java內(nèi)存泄漏引起的原因

內(nèi)存泄漏是指無(wú)用對(duì)象(不再使用的對(duì)象)持續(xù)占有內(nèi)存或無(wú)用對(duì)象的內(nèi)存得不到及時(shí)釋放爆惧,從而造成內(nèi)存空間的浪費(fèi)稱為內(nèi)存泄漏狸页。內(nèi)存泄露有時(shí)不嚴(yán)重且不易察覺(jué),這樣開(kāi)發(fā)者就不知道存在內(nèi)存泄露,但有時(shí)也會(huì)很嚴(yán)重芍耘,會(huì)提示你Out of memory址遇。

Java內(nèi)存泄漏的根本原因是什么呢?長(zhǎng)生命周期的對(duì)象持有短生命周期對(duì)象的引用就很可能發(fā)生內(nèi)存泄漏斋竞,盡管短生命周期對(duì)象已經(jīng)不再需要倔约,但是因?yàn)殚L(zhǎng)生命周期持有它的引用而導(dǎo)致不能被回收,這就是Java中內(nèi)存泄漏的發(fā)生場(chǎng)景坝初。

來(lái)先看看下面的例子浸剩,為什么會(huì)發(fā)生內(nèi)存泄漏。下面這個(gè)例子中鳄袍,A對(duì)象引用B對(duì)象绢要,A對(duì)象的生命周期(t1-t4)比B對(duì)象的生命周期(t2-t3)長(zhǎng)的多。當(dāng)B對(duì)象沒(méi)有被應(yīng)用程序使用之后拗小,A對(duì)象仍然在引用著B(niǎo)對(duì)象重罪。這樣,垃圾回收器就沒(méi)辦法將B對(duì)象從內(nèi)存中移除哀九,從而導(dǎo)致內(nèi)存問(wèn)題剿配,因?yàn)槿绻鸄引用更多這樣的對(duì)象,那將有更多的未被引用對(duì)象存在勾栗,并消耗內(nèi)存空間惨篱。

B對(duì)象也可能會(huì)持有許多其他的對(duì)象,那這些對(duì)象同樣也不會(huì)被垃圾回收器回收围俘。所有這些沒(méi)在使用的對(duì)象將持續(xù)的消耗之前分配的內(nèi)存空間砸讳。

生命周期圖

具體主要有如下幾大類:

2.2.1 靜態(tài)集合類引起內(nèi)存泄漏

像HashMap、Vector等的使用最容易出現(xiàn)內(nèi)存泄露界牡,這些靜態(tài)變量的生命周期和應(yīng)用程序一致簿寂,他們所引用的所有的對(duì)象Object也不能被釋放,因?yàn)樗麄円矊⒁恢北籚ector等引用著宿亡。

例如:

Static Vector v = new Vector(10);

for (int i = 0; i < 100; i++) {
    Object o = new Object();
    v.add(o);
    o = null;
}

在這個(gè)例子中常遂,循環(huán)申請(qǐng)Object 對(duì)象,并將所申請(qǐng)的對(duì)象放入一個(gè)Vector 中挽荠,如果僅僅釋放引用本身(o=null)克胳,那么Vector 仍然引用該對(duì)象,所以這個(gè)對(duì)象對(duì)GC 來(lái)說(shuō)是不可回收的圈匆。因此漠另,如果對(duì)象加入到Vector 后,還必須從Vector 中刪除跃赚,最簡(jiǎn)單的方法就是將Vector對(duì)象設(shè)置為null笆搓。

2.2.2 監(jiān)聽(tīng)器

java 編程中,我們都需要和監(jiān)聽(tīng)器打交道,通常一個(gè)應(yīng)用當(dāng)中會(huì)用到很多監(jiān)聽(tīng)器满败,我們會(huì)調(diào)用一個(gè)控件的諸如addXXXListener() 等方法來(lái)增加監(jiān)聽(tīng)器肤频,但往往在釋放對(duì)象的時(shí)候卻沒(méi)有記住去刪除這些監(jiān)聽(tīng)器,從而增加了內(nèi)存泄漏的機(jī)會(huì)算墨。

2.2.3 各種連接

比如數(shù)據(jù)庫(kù)連接(dataSourse.getConnection())宵荒,網(wǎng)絡(luò)連接(socket)和io連接,除非其顯式的調(diào)用了其close() 方法將其連接關(guān)閉米同,否則是不會(huì)自動(dòng)被GC 回收的骇扇。對(duì)于ResultsetStatement 對(duì)象可以不進(jìn)行顯式回收,但Connection 一定要顯式回收面粮,因?yàn)?strong>Connection 在任何時(shí)候都無(wú)法自動(dòng)回收少孝,而Connection一旦回收,ResultsetStatement 對(duì)象就會(huì)立即為NULL熬苍。但是如果使用連接池稍走,情況就不一樣了,除了要顯式地關(guān)閉連接柴底,還必須顯式地關(guān)閉Resultset Statement 對(duì)象(關(guān)閉其中一個(gè)婿脸,另外一個(gè)也會(huì)關(guān)閉)柄驻,否則就會(huì)造成大量的Statement 對(duì)象無(wú)法釋放,從而引起內(nèi)存泄漏鸿脓。這種情況下一般都會(huì)在try 里面去的連接,在finally里面釋放連接野哭。

2.2.4 內(nèi)部類和外部模塊的引用

內(nèi)部類的引用是比較容易遺忘的一種在塔,而且一旦沒(méi)釋放可能導(dǎo)致一系列的后繼類對(duì)象沒(méi)有釋放。此外程序員還要小心外部模塊不經(jīng)意的引用拨黔,例如程序員A 負(fù)責(zé)A 模塊蛔溃,調(diào)用了B 模塊的一個(gè)方法如:

public void registerMsg(Object b);

這種調(diào)用就要非常小心了,傳入了一個(gè)對(duì)象篱蝇,很可能模塊B就保持了對(duì)該對(duì)象的引用贺待,這時(shí)候就需要注意模塊B是否提供相應(yīng)的操作去除引用。

2.2.5 單例模式

不正確使用單例模式是引起內(nèi)存泄漏的一個(gè)常見(jiàn)問(wèn)題零截,單例對(duì)象在初始化后將在 JVM 的整個(gè)生命周期中存在(以靜態(tài)變量的方式)狠持,如果單例對(duì)象持有外部的引用,那么這個(gè)對(duì)象將不能被 JVM 正痴叭螅回收,導(dǎo)致內(nèi)存泄漏,考慮下面的例子:

public 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;
    }

    public A getA() {
        return a;
    }
}

3. Java 內(nèi)存分配策略

Java 程序運(yùn)行時(shí)的內(nèi)存分配策略有三種,分別是靜態(tài)分配,棧式分配,和堆式分配绍撞,對(duì)應(yīng)的正勒,三種存儲(chǔ)策略使用的內(nèi)存空間主要分別是靜態(tài)存儲(chǔ)區(qū)(也稱方法區(qū))、棧區(qū)和堆區(qū)傻铣。

靜態(tài)存儲(chǔ)區(qū)(方法區(qū)):主要存放靜態(tài)數(shù)據(jù)章贞、全局 static 數(shù)據(jù)和常量。這塊內(nèi)存在程序編譯時(shí)就已經(jīng)分配好非洲,并且在程序整個(gè)運(yùn)行期間都存在鸭限。

棧區(qū) :當(dāng)方法被執(zhí)行時(shí),方法體內(nèi)的局部變量(其中包括基礎(chǔ)數(shù)據(jù)類型两踏、對(duì)象的引用)都在棧上創(chuàng)建败京,并在方法執(zhí)行結(jié)束時(shí)這些局部變量所持有的內(nèi)存將會(huì)自動(dòng)被釋放。因?yàn)闂?nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中梦染,效率很高赡麦,但是分配的內(nèi)存容量有限。

堆區(qū) : 又稱動(dòng)態(tài)內(nèi)存分配帕识,通常就是指在程序運(yùn)行時(shí)直接 new 出來(lái)的內(nèi)存泛粹,也就是對(duì)象的實(shí)例。這部分內(nèi)存在不使用時(shí)將會(huì)由 Java 垃圾回收器來(lái)負(fù)責(zé)回收晶姊。

3.1 棧與堆的區(qū)別

在方法體內(nèi)定義的(局部變量)一些基本類型的變量和對(duì)象的引用變量都是在方法的棧內(nèi)存中分配的们衙。當(dāng)在一段方法塊中定義一個(gè)變量時(shí)砍艾,Java 就會(huì)在棧中為該變量分配內(nèi)存空間巍举,當(dāng)超過(guò)該變量的作用域后懊悯,該變量也就無(wú)效了,分配給它的內(nèi)存空間也將被釋放掉炭分,該內(nèi)存空間可以被重新使用。

堆內(nèi)存用來(lái)存放所有由 new 創(chuàng)建的對(duì)象(包括該對(duì)象其中的所有成員變量)和數(shù)組观堂。在堆中分配的內(nèi)存,將由 Java 垃圾回收器來(lái)自動(dòng)管理溃睹。在堆中產(chǎn)生了一個(gè)數(shù)組或者對(duì)象后因篇,還可以在棧中定義一個(gè)特殊的變量竞滓,這個(gè)變量的取值等于數(shù)組或者對(duì)象在堆內(nèi)存中的首地址商佑,這個(gè)特殊的變量就是我們上面說(shuō)的引用變量莉御。我們可以通過(guò)這個(gè)引用變量來(lái)訪問(wèn)堆中的對(duì)象或者數(shù)組礁叔。

舉個(gè)栗子:

public class Sample {
    int s1 = 0;
    Sample mSample1 = new Sample();
    
    public void method() {
        int s2 = 1;
        Sample mSample2 = new Sample();
    }
}
Sample mSample3 = new Sample();

Sample 類的局部變量 s2 和引用變量 mSample2 都是存在于棧中琅关,但 mSample2 指向的對(duì)象是存在于堆上的讥蔽。

mSample3 指向的對(duì)象實(shí)體存放在堆上新症,包括這個(gè)對(duì)象的所有成員變量 s1 和 mSample1响禽,而它自己存在于棧中芋类。

結(jié)論
局部變量的基本數(shù)據(jù)類型和引用存儲(chǔ)于中侯繁,引用的對(duì)象實(shí)體存儲(chǔ)于中贮竟。—— 因?yàn)樗鼈儗儆诜椒ㄖ械淖兞看饫粒芷陔S方法而結(jié)束。
成員變量全部存儲(chǔ)于中(包括基本數(shù)據(jù)類型弓颈,引用和引用的對(duì)象實(shí)體)—— 因?yàn)樗鼈儗儆陬愊杓剑悓?duì)象終究是要被new出來(lái)使用的纤子。

了解了 Java 的內(nèi)存分配之后控硼,我們?cè)賮?lái)看看 Java 是怎么管理內(nèi)存的。

3.2 Java如何管理內(nèi)存

Java的內(nèi)存管理就是對(duì)象的分配和釋放問(wèn)題翼悴。在 Java 中,程序員需要通過(guò)關(guān)鍵字 new 為每個(gè)對(duì)象申請(qǐng)內(nèi)存空間 (基本類型除外)误堡,所有的對(duì)象都在堆 (Heap)中分配空間锁施。另外沾谜,對(duì)象的釋放是由 GC 決定和執(zhí)行的。在 Java 中婚温,內(nèi)存的分配是由程序完成的栅螟,而內(nèi)存的釋放是由 GC 完成的,這種收支兩條線的方法確實(shí)簡(jiǎn)化了程序員的工作步绸。但同時(shí)瓤介,它也加重了JVM的工作刑桑。這也是 Java 程序運(yùn)行速度較慢的原因之一祠斧。因?yàn)?strong>GC 為了能夠正確釋放對(duì)象琢锋,GC 必須監(jiān)控每一個(gè)對(duì)象的運(yùn)行狀態(tài)吴超,包括對(duì)象的申請(qǐng)、引用飒责、被引用宏蛉、賦值等揍堰,GC 都需要進(jìn)行監(jiān)控屏歹。

監(jiān)視對(duì)象狀態(tài)是為了更加準(zhǔn)確地蝙眶、及時(shí)地釋放對(duì)象幽纷,而釋放對(duì)象的根本原則就是該對(duì)象不再被引用友浸。

為了更好理解 GC 的工作原理,我們可以將對(duì)象考慮為有向圖的頂點(diǎn)武学,將引用關(guān)系考慮為圖的有向邊劳淆,有向邊從引用者指向被引對(duì)象括勺。另外缆八,每個(gè)線程對(duì)象可以作為一個(gè)圖的起始頂點(diǎn)奈辰,例如大多程序從 main 進(jìn)程開(kāi)始執(zhí)行乱豆,那么該圖就是以 main 進(jìn)程頂點(diǎn)開(kāi)始的一棵根樹(shù)奖恰。在這個(gè)有向圖中,根頂點(diǎn)可達(dá)的對(duì)象都是有效對(duì)象宛裕,GC將不回收這些對(duì)象瑟啃。如果某個(gè)對(duì)象 (連通子圖)與這個(gè)根頂點(diǎn)不可達(dá)(注意,該圖為有向圖)揩尸,那么我們認(rèn)為這個(gè)(這些)對(duì)象不再被引用蛹屿,可以被 GC 回收。

以下岩榆,我們舉一個(gè)例子說(shuō)明如何用有向圖表示內(nèi)存管理错负。對(duì)于程序的每一個(gè)時(shí)刻犹撒,我們都有一個(gè)有向圖表示JVM的內(nèi)存分配情況谊囚。以下右圖,就是左邊程序運(yùn)行到第6行的示意圖。

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Object o1 = new Object();
        Object o2 = new Object();
        o2 = o1;//此行為第6行
    }
}
有向圖

4. 如何防止內(nèi)存泄漏的發(fā)生滤否?

在了解了引起內(nèi)存泄漏的一些原因后欲芹,應(yīng)該盡可能地避免和發(fā)現(xiàn)內(nèi)存泄漏浙宜。

4.1 好的編碼習(xí)慣

最基本的建議就是盡早釋放無(wú)用對(duì)象的引用亩钟,大多數(shù)程序員在使用臨時(shí)變量的時(shí)候臭觉,都是讓引用變量在退出活動(dòng)域后,自動(dòng)設(shè)置為 null 。在使用這種方式時(shí)候忆某,必須特別注意一些復(fù)雜的對(duì)象圖,例如數(shù)組、列喂窟、樹(shù)稳摄、圖等仗阅,這些對(duì)象之間有相互引用關(guān)系較為復(fù)雜醋闭。對(duì)于這類對(duì)象瑟曲,GC 回收它們一般效率較低烦衣。如果程序允許,盡早將不用的引用對(duì)象賦為null。另外建議幾點(diǎn):

在確認(rèn)一個(gè)對(duì)象無(wú)用后今布,將其所有引用顯式的置為null拭抬;

當(dāng)類從 JpanelJdialog 或其它容器類繼承的時(shí)候部默,刪除該對(duì)象之前不妨調(diào)用它的 removeall() 方法傅蹂;在設(shè)一個(gè)引用變量為 null 值之前请敦,應(yīng)注意該引用變量指向的對(duì)象是否被監(jiān)聽(tīng),若有,要首先除去監(jiān)聽(tīng)器,然后才可以賦空值娜睛;當(dāng)對(duì)象是一個(gè) Thread 的時(shí)候芦疏,刪除該對(duì)象之前不妨調(diào)用它的
interrupt() 方法;內(nèi)存檢測(cè)過(guò)程中不僅要關(guān)注自己編寫的類對(duì)象微姊,同時(shí)也要關(guān)注一些基本類型的對(duì)象,例如:int[]分预、String兢交、char[] 等等;如果有數(shù)據(jù)庫(kù)連接笼痹,使用 try…finally 結(jié)構(gòu)配喳,在 finally 中關(guān)閉 Statement 對(duì)象和連接。

4.2 好的測(cè)試工具

在開(kāi)發(fā)中不能完全避免內(nèi)存泄漏凳干,關(guān)鍵要在發(fā)現(xiàn)有內(nèi)存泄漏的時(shí)候能用好的測(cè)試工具迅速定位問(wèn)題的所在晴裹。市場(chǎng)上已有幾種專業(yè)檢查 Java 內(nèi)存泄漏的工具,它們的基本工作原理大同小異救赐,都是通過(guò)監(jiān)測(cè) Java 程序運(yùn)行時(shí)涧团,所有對(duì)象的申請(qǐng)、釋放等動(dòng)作,將內(nèi)存管理的所有信息進(jìn)行統(tǒng)計(jì)泌绣、分析钮追、可視化。開(kāi)發(fā)人員將根據(jù)這些信息判斷程序是否有內(nèi)存泄漏問(wèn)題阿迈。這些工具包括 Optimizeit Profiler元媚、JProbe Profiler、JinSight苗沧、Rational 公司的 Purify 等刊棕。

4.3 注意像 HashMapArrayList 的集合對(duì)象

特別注意一些像 HashMap 待逞、ArrayList 的集合對(duì)象甥角,它們經(jīng)常會(huì)引發(fā)內(nèi)存泄漏。當(dāng)它們被聲明為 static 時(shí)飒焦,它們的生命周期就會(huì)和應(yīng)用程序一樣長(zhǎng)蜈膨。

4.4 注意 事件監(jiān)聽(tīng)回調(diào)函數(shù)

特別注意 事件監(jiān)聽(tīng)回調(diào)函數(shù) 。當(dāng)一個(gè)監(jiān)聽(tīng)器在使用的時(shí)候被注冊(cè)牺荠,但不再使用之后卻未被反注冊(cè)翁巍。

“如果一個(gè)類自己管理內(nèi)存,那開(kāi)發(fā)人員就得小心內(nèi)存泄漏問(wèn)題了休雌≡詈” 通常一些成員變量引用其他對(duì)象,初始化的時(shí)候需要置空杈曲。

參考文章:
1.介紹Java中的內(nèi)存泄漏
2.Java的內(nèi)存泄漏
3.Java中關(guān)于內(nèi)存泄漏出現(xiàn)的原因匯總及如何避免內(nèi)存泄漏
4.Java內(nèi)存泄漏的幾大原因及預(yù)防檢測(cè)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驰凛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子担扑,更是在濱河造成了極大的恐慌恰响,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涌献,死亡現(xiàn)場(chǎng)離奇詭異胚宦,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)燕垃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門枢劝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人卜壕,你說(shuō)我怎么就攤上這事您旁。” “怎么了轴捎?”我有些...
    開(kāi)封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵鹤盒,是天一觀的道長(zhǎng)蚕脏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)昨悼,這世上最難降的妖魔是什么蝗锥? 我笑而不...
    開(kāi)封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮率触,結(jié)果婚禮上终议,老公的妹妹穿的比我還像新娘。我一直安慰自己葱蝗,他們只是感情好穴张,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著两曼,像睡著了一般皂甘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悼凑,一...
    開(kāi)封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天偿枕,我揣著相機(jī)與錄音,去河邊找鬼户辫。 笑死渐夸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的渔欢。 我是一名探鬼主播墓塌,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奥额!你這毒婦竟也來(lái)了苫幢?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤垫挨,失蹤者是張志新(化名)和其女友劉穎韩肝,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體九榔,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伞梯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帚屉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡漾峡,死狀恐怖攻旦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情生逸,我是刑警寧澤牢屋,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布且预,位于F島的核電站,受9級(jí)特大地震影響烙无,放射性物質(zhì)發(fā)生泄漏锋谐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一截酷、第九天 我趴在偏房一處隱蔽的房頂上張望涮拗。 院中可真熱鬧,春花似錦迂苛、人聲如沸三热。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)就漾。三九已至,卻和暖如春念搬,著一層夾襖步出監(jiān)牢的瞬間抑堡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工朗徊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留首妖,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓荣倾,卻偏偏與公主長(zhǎng)得像悯搔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舌仍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容