關於編程內存泄漏

首先,以java爲例,說下gc的有向圖回收機制html

1 引言
     Java的一個重要優勢就是經過垃圾收集器GC (Garbage Collection)自動管理內存的回收,程序員不須要經過調用函數來釋放內存。所以,不少程序員認爲Java 不存在內存泄漏問題,或者認爲即便有內存泄漏也不是程序的責任,而是GC 或JVM的問題。其實,這種想法是不正確的,由於Java 也存在內存泄漏,但它的表現與C++不一樣。若是正在開發的Java 代碼要全天24 小時在服務器上運行,則內存漏洞在此處的影響就比在配置實用程序中的影響要大得多,即便最小的漏洞也會致使JVM耗盡所有可用內存。另外,在不少嵌入式系統中,內存的總量很是有限。在相反的狀況下,即使程序的生存期較短,若是存在分配大量臨時對象(或者若干吞噬大量內存的對象)的任何Java 代碼,並且當再也不須要這些對象時也沒有取消對它們的引用,則仍然可能達到內存極限。

2 Java 內存回收機制
     Java 的內存管理就是對象的分配和釋放問題。分配內存的方式多種多樣,取決於該種語言的語法結構。但不管是哪種語言的內存分配方式,最後都要返回所分配的內存塊的起始地址,即返回一個指針到內存塊的首地址。在Java 中全部對象都是在堆(Heap)中分配的,對象的建立一般都是採用new或者是反射的方式,但對象釋放卻有直接的手段,因此對象的回收都是由Java虛擬機經過垃圾收集器去完成的。這種收支兩條線的方法確實簡化了程序員的工做,但同時也加劇了JVM的工做,這也是Java 程序運行速度較慢的緣由之一。由於,GC 爲了可以正確釋放對象,GC 必須監控每個對象的運行狀態,包括對象的申請、引用、被引用、賦值等,GC 都須要進行監控。監視對象狀態是爲了更加準確地、及時地釋放對象,而釋放對象的根本原則就是該對象再也不被引用。Java 使用有向圖的方式進行內存管理,能夠消除引用循環的問題,例若有三個對象,相互引用,只要它們和根進程不可達,那麼GC 也是能夠回收它們的。在Java 語言中,判斷一塊內存空間是否符合垃圾收集器收集標準的標準只有兩個:一個是給對象賦予了空值null,如下再沒有調用過,另外一個是給對象賦予了新值,即從新分配了內存空間。

3 Java 中的內存泄漏
3.1 Java 中內存泄漏與C++的區別
    在Java 中,內存泄漏就是存在一些被分配的對象,這些對象有下面兩個特色,首先,這些對象是可達的,即在有向圖中,存在通路能夠與其相連;其次,這些對象是無用的,即程序之後不會再使用這些對象。若是對象知足這兩個條件,這些對象就能夠斷定爲Java 中的內存泄漏,這些對象不會被GC 所回收,然而它卻佔用內存。在C++中,內存泄漏的範圍更大一些。有些對象被分配了內存空間,而後卻不可達,因爲C++中沒有GC,這些內存將永遠收不回來。在Java 中,這些不可達的對象都由GC 負責回收,所以程序員不須要考慮這部分的內存泄漏。經過分析,能夠得知,對於C++,程序員須要本身管理邊和頂點,而對於Java 程序員只須要管理邊就能夠了(不須要管理頂點
的釋放)。經過這種方式,Java 提升了編程的效率。

3.2 內存泄漏示例
3.2.1 示例1
   在這個例子中,循環申請Object 對象,並將所申請的對象放入一個Vector 中,若是僅僅釋放引用自己,那麼Vector 仍然引用該對象,因此這個對象對GC 來講是不可回收的。所以,若是對象加入到Vector 後,還必須從Vector 中刪除,最簡單的方法就是將Vector對象設置爲null。
Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{Object o = new Object();
v.add(o);
o = null;
}//

此時,全部的Object 對象都沒有被釋放,由於變量v 引用這些對象。實際上無用,而還被引用的對象,GC 就無能爲力了(事實上GC 認爲它還有用),這一點是致使內存泄漏最重要的緣由。

(1)若是要釋放對象,就必須使其的引用記數爲0,只有那些再也不被引用的對象才能被釋放,這個原理很簡單,可是很重要,是致使內存泄漏的基本緣由,也是解決內存泄漏方法的宗旨;
(2)程序員無須管理對象空間具體的分配和釋放過程,但必需要關注被釋放對象的引用記數是否爲0;
(3)一個對象可能被其餘對象引用的過程的幾種:
a.直接賦值,如上例中的A.a = E;
b.經過參數傳遞,例如public void addObject(Object E);
c.其它一些狀況如系統調用等。

3.3 容易引發內存泄漏的幾大緣由
3.3.1 靜態集合類
      像HashMap、Vector 等靜態集合類的使用最容易引發內存泄漏,由於這些靜態變量的生命週期與應用程序一致,如示例1,若是該Vector 是靜態的,那麼它將一直存在,而其中全部的Object對象也不能被釋放,由於它們也將一直被該Vector 引用着。
3.3.2 監聽器
     在java 編程中,咱們都須要和監聽器打交道,一般一個應用當中會用到不少監聽器,咱們會調用一個控件的諸如addXXXListener()等方法來增長監聽器,但每每在釋放對象的時候卻沒有記住去刪除這些監聽器,從而增長了內存泄漏的機會。
3.3.3 物理鏈接
         一些物理鏈接,好比數據庫鏈接和網絡鏈接,除非其顯式的關閉了鏈接,不然是不會自動被GC 回收的。Java 數據庫鏈接通常用DataSource.getConnection()來建立,當再也不使用時必須用Close()方法來釋放,由於這些鏈接是獨立於JVM的。對於Resultset 和Statement 對象能夠不進行顯式回收,但Connection 必定要顯式回收,由於Connection 在任什麼時候候都沒法自動回收,而Connection一旦回收,Resultset 和Statement 對象就會當即爲NULL。可是若是使用鏈接池,狀況就不同了,除了要顯式地關閉鏈接,還必須顯式地關閉Resultset Statement 對象(關閉其中一個,另一個也會關閉),不然就會形成大量的Statement 對象沒法釋放,從而引發內存泄漏。

3.3.4 內部類和外部模塊等的引用
        內部類的引用是比較容易遺忘的一種,並且一旦沒釋放可能致使一系列的後繼類對象沒有釋放。對於程序員而言,本身的程序很清楚,若是發現內存泄漏,本身對這些對象的引用能夠很快定位並解決,可是如今的應用軟件並不是一我的實現,模塊化的思想在現代軟件中很是明顯,因此程序員要當心外部模塊不經意的引用,例如程序員A 負責A 模塊,調用了B 模塊的一個方法如:public void registerMsg(Object b);這種調用就要很是當心了,傳入了一個對象,極可能模塊B就保持了對該對象的引用,這時候就須要注意模塊B 是否提供相應的操做去除引用。

4 預防和檢測內存漏洞
    在瞭解了引發內存泄漏的一些緣由後,應該儘量地避免和發現內存泄漏。
(1)好的編碼習慣。最基本的建議就是儘早釋放無用對象的引用,大多數程序員在使用臨時變量的時候,都是讓引用變量在退出活動域後,自動設置爲null。在使用這種方式時候,必須特別注意一些複雜的對象圖,例如數組、列、樹、圖等,這些對象之間有相互引用關係較爲複雜。對於這類對象,GC 回收它們通常效率較低。若是程序容許,儘早將不用的引用對象賦爲null。另外建議幾點:
在確認一個對象無用後,將其全部引用顯式的置爲null;
當類從Jpanel 或Jdialog 或其它容器類繼承的時候,刪除該對象以前不妨調用它的removeall()方法;在設一個引用變量爲null 值以前,應注意該引用變量指向的對象是否被監聽,如有,要首先除去監聽器,而後才能夠賦空值;當對象是一個Thread 的時候,刪除該對象以前不妨調用它的interrupt()方法;內存檢測過程當中不只要關注本身編寫的類對象,同時也要關注一些基本類型的對象,例如:int[]、String、char[]等等;若是有數據庫鏈接,使用try...finally 結構,在finally 中關閉Statement 對象和鏈接。
(2)好的測試工具。在開發中不能徹底避免內存泄漏,關鍵要在發現有內存泄漏的時候能用好的測試工具迅速定位問題的所在。市場上已有幾種專業檢查Java 內存泄漏的工具,它們的基本工做原理大同小異,都是經過監測Java 程序運行時,全部對象的申請、釋放等動做,將內存管理的全部信息進行統計、分析、可視化。開發人員將根據這些信息判斷程序是否有內存泄漏問題。這些工具包括Optimizeit Profiler、JProbe Profiler、JinSight、Rational 公司的Purify 等。java

下面說下python的垃圾回收機制python

python裏也同java同樣採用了垃圾收集機制,不過不同的是,python採用的是引用計數機制爲主,標記-清除和分代收集兩種機制爲輔的策略。程序員

引用計數機制的優勢:數據庫

一、簡單
二、實時性:一旦沒有引用,內存就直接釋放了。不用像其餘機制等到特定時機。實時性還帶來一個好處:處理回收內存的時間分攤到了平時。        
引用計數機制的缺點: 
一、維護引用計數消耗資源 
二、循環引用 
 
下面說下什麼事標記清除和分代收集
一、標記-清除機制

標記-清除機制,顧名思義,首先標記對象(垃圾檢測),而後清除垃圾(垃圾回收)。如圖1:編程

image

                            圖1數組

首先初始全部對象標記爲白色,並肯定根節點對象(這些對象是不會被刪除),標記它們爲黑色(表示對象有效)。將有效對象引用的對象標記爲灰色(表示對象可達,服務器

但它們所引用的對象還沒檢查),檢查完灰色對象引用的對象後,將灰色標記爲黑色。重複直到不存在灰色節點爲止。最後白色結點都是須要清除的對象。網絡

二、回收對象的組織模塊化

這裏所採用的高級機制做爲引用計數的輔助機制,用於解決產生的循環引用問題。而循環引用只會出如今「內部存在能夠對其餘對象引用的對象」,好比:list,class等。

爲了要將這些回收對象組織起來,須要創建一個鏈表。天然,每一個被收集的對象內就須要多提供一些信息,下面代碼是回收對象裏必然出現的。

一個對象的實際結構如圖2:

                  image

                           圖2

經過PyGC_Head的指針將每一個回收對象鏈接起來,造成了一個鏈表,也就是在1裏提到的初始化的全部對象。

三、分代技術

分代技術是一種典型的以空間換時間的技術,這也正是java裏的關鍵技術。這種思想簡單點說就是:對象存在時間越長,越可能不是垃圾,應該越少去收集。

這樣的思想,能夠減小標記-清除機制所帶來的額外操做。分代就是將回收對象分紅數個代,每一個代就是一個鏈表(集合),代進行標記-清除的時間與代內對象

存活時間成正比例關係。

從上面代碼能夠看出python裏一共有三代,每一個代的threshold值表示該代最多容納對象的個數。默認狀況下,當0代超過700,或1,2代超過10,垃圾回收機制將觸發。

0代觸發將清理全部三代,1代觸發會清理1,2代,2代觸發後只會清理本身。

轉載自:http://www.cnblogs.com/mingaixin/archive/2013/01/31/2886680.html

相關文章
相關標籤/搜索