<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Rex

    ——生命不止,奮斗不息。
    posts - 27, comments - 8, trackbacks - 0, articles - 0
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    關于內存泄露

    Posted on 2006-11-22 14:13 W.R 閱讀(547) 評論(0)  編輯  收藏 所屬分類: 支持技術
    一、C++
    ?????????一般我們常說的內存泄漏是指堆內存的泄漏。堆內存是指程序從堆中分配的,大小任意的(內存塊的大小可以在程序運行期決定),使用完后必須顯示釋放的內存。應用程序一般使用malloc,realloc,new等函數從堆中分配到一塊內存,使用完后,程序必須負責相應的調用free或delete釋放該內存塊,否則,這塊內存就不能被再次使用,我們就說這塊內存泄漏了。看如下一段Ccode:
    void?GetMemory2(char?**p,?int?num)?
    {?
    ????
    *p?=?(char?*)malloc(num);?
    }?
    void?Test(void)?
    {?
    ????char?
    *str?=?NULL;?
    ????GetMemory(
    &str,?100);?
    ????strcpy(str,?
    "hello");?
    ????printf(str);?
    }?
    執行Test后,能夠輸出hello ;但是確實是內存泄露了,該free一下,另外我想C++中free is better than delete!
    ?????????以下這段小程序演示了堆內存發生泄漏的情形:
    void?MyFunction(int?nSize)
    {
    ???????
    char*??p=?new?char[nSize];
    ???????
    if(?!GetStringFrom(?p,?nSize?)?){
    ??????????????MessageBox(“Error”);
    ??????????????
    return;
    ???????}

    ???????…
    //using?the?string?pointed?by?p;
    ???????delete?p;
    }


    ?????????廣義的說,內存泄漏不僅僅包含堆內存的泄漏,還包含系統資源的泄漏(resource leak),比如核心態HANDLE,GDI Object,SOCKET, Interface等,從根本上說這些由操作系統分配的對象也消耗內存,如果這些對象發生泄漏最終也會導致內存的泄漏。而且,某些對象消耗的是核心態內存,這些對象嚴重泄漏時會導致整個操作系統不穩定。所以相比之下,系統資源的泄漏比堆內存的泄漏更為嚴重。

    GDI Object的泄漏是一種常見的資源泄漏:

    ?

    void?CMyView::OnPaint(?CDC*?pDC?)
    {
    ???????CBitmap?bmp;
    ???????CBitmap
    *?pOldBmp;
    ???????bmp.LoadBitmap(IDB_MYBMP);
    ???????pOldBmp?
    =?pDC->SelectObject(?&bmp?);
    ???????…
    ???????
    if(?Something()?){
    ??????????????
    return;
    ???????}

    ???????pDC
    ->SelectObject(?pOldBmp?);
    ???????
    return;
    }

    當函數Something()返回非零的時候,程序在退出前沒有把pOldBmp選回pDC中,這會導致pOldBmp指向的HBITMAP對象發生泄漏。這個程序如果長時間的運行,可能會導致整個系統花屏。這種問題在Win9x下比較容易暴露出來,因為Win9x的GDI堆比Win2k或NT的要小很多。
    ?????????有一個很簡單的辦法來檢查一個程序是否有內存泄漏.就是是用Windows的任務管理器(Task Manager).??運行程序,然后在任務管理器里面查看 “內存使用”和”虛擬內存大小”兩項,當程序請求了它所需要的內存之后,如果虛擬內存還是持續的增長的話,就說明了這個程序有內存泄漏問題. 當然如果內存泄漏的數目非常的小,用這種方法可能要過很長時間才能看的出來.
    ?????????已經有許多技術被研究出來以應對這個問題,比如Smart Pointer,Garbage Collection等。Smart Pointer技術比較成熟,STL中已經包含支持Smart Pointer的class,但是它的使用似乎并不廣泛,而且它也不能解決所有的問題;Garbage Collection技術在Java中已經比較成熟,但是在c/c++領域的發展并不順暢,雖然很早就有人思考在C++中也加入GC的支持。現實世界就是這樣的,作為一個c/c++程序員,內存泄漏是你心中永遠的痛。

    以發生的方式來分類,內存泄漏可以分為4類:

    ??????1.常發性內存泄漏。發生內存泄漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存泄漏。比如例二,如果Something()函數一直返回True,那么pOldBmp指向的HBITMAP對象總是發生泄漏。

    ??????2.偶發性內存泄漏。發生內存泄漏的代碼只有在某些特定環境或操作過程下才會發生。比如例二,如果Something()函數只有在特定環境下才返回True,那么pOldBmp指向的HBITMAP對象并不總是發生泄漏。常發性和偶發性是相對的。對于特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測內存泄漏至關重要。

    ??????3.一次性內存泄漏。發生內存泄漏的代碼只會被執行一次,或者由于算法上的缺陷,導致總會有一塊僅且一塊內存發生泄漏。比如,在類的構造函數中分配內存,在析構函數中卻沒有釋放該內存,但是因為這個類是一個Singleton,所以內存泄漏只會發生一次。另一個例子:

    ?

    char*?g_lpszFileName?=?NULL;
    void?SetFileName(?const?char*?lpcszFileName?)
    {
    ????
    if(?g_lpszFileName?){
    ????????free(?g_lpszFileName?);
    ????}

    ????g_lpszFileName?
    =?strdup(?lpcszFileName?);
    }

    如果程序在結束的時候沒有釋放g_lpszFileName指向的字符串,那么,即使多次調用SetFileName(),總會有一塊內存,而且僅有一塊內存發生泄漏。

    ??????4.隱式內存泄漏。程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這里并沒有發生內存泄漏,因為最終程序釋放了所有申請的內存。但是對于一個服務器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存泄漏為隱式內存泄漏。舉一個例子:

    ?

    class?Connection
    {
    public:
    ???????Connection(?SOCKET?s);
    ???????
    ~Connection();
    ???????…
    private:
    ???????SOCKET?_socket;
    ???????…
    }
    ;
    class?ConnectionManager
    {
    public:
    ???????ConnectionManager()
    {

    ???????}

    ???????
    ~ConnectionManager(){
    ??????????list
    <Connection>::iterator?it;
    ??????????
    for(?it?=?_connlist.begin();?it?!=?_connlist.end();?++it?){
    ?????????????????????delete?(
    *it);
    ??????????????}

    ??????????????_connlist.clear();
    ???????}

    ???????
    void?OnClientConnected(?SOCKET?s?){
    ?????????Connection
    *?p?=?new?Connection(s);
    ?????????_connlist.push_back(p);
    ???????}

    ???????
    void?OnClientDisconnected(?Connection*?pconn?){
    ??????????????_connlist.remove(?pconn?);
    ??????????????delete?pconn;
    ???????}

    private:
    ???????list
    <Connection*>?_connlist;
    }
    ;

    ?假設在Client從Server端斷開后,Server并沒有呼叫OnClientDisconnected()函數,那么代表那次連接的Connection對象就不會被及時的刪除(在Server程序退出的時候,所有Connection對象會在ConnectionManager的析構函數里被刪除)。當不斷的有連接建立、斷開時隱式內存泄漏就發生了。

    二、JAVA中的內存泄露
    ?????????JAVA有GC自動回收內存,內存泄露是指系統中存在無法回收的內存,有時候會造成內存不足或系統崩潰。在C/C++中分配了內存不釋放的情況就是內存泄露。雖然Java存在內存泄露,但是基本上不用很關心它,特別是那些對代碼本身就不講究的就更不要去關心這個了。 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.arraycopy(oldElements,
    0,?elements,?0,?size);
     ??}

    ??}

    }

    假如堆棧加了10個元素,然后全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內存泄露的兩個條件:無用,無法回收。再看這個例子:
    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個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的進取,以前的引用自然消失!for example:
    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循環也不會有
     
    //什么太大的影響吧,你會這么做嗎?
    }
    上面兩個例子都不過是小打小鬧,但是C/C++中的內存泄露就不是Bad了,而是Worst了。他們如果一處沒有回收就永遠無法回收,頻繁的調用這個方法內存不就用光了!因為Java還有自愈功能(我自己起的名字,還沒申請專利),所以Java的內存泄露問題幾乎可以忽略了,但是知道的人就不要犯了。
      不知者無罪!Java存在內存泄露,但是也不要夸大其辭。如果你對Java都不是很熟,你根本就不用關心這個,我說過你無意中寫出內存泄露的例子就像你中一千萬一樣概率小,開玩笑了,其實應該是小的多的多!?
    ?????????在某些時候,因為代碼上寫的有問題,會導致某些內存想回收都收不回來,比如下面的代碼
    Temp1?=?new?BYTE[100];
    Temp2?
    =?new?BYTE[100];
    Temp2?
    =?Temp1;
    這樣,Temp2的內存地址就丟掉了,而且永遠都找不回了,這個時候Temp2的內存空間想回收都沒有辦法.

    主站蜘蛛池模板: 国产香蕉九九久久精品免费| 又爽又黄无遮挡高清免费视频| 亚洲天堂免费在线| 国产伦精品一区二区三区免费下载 | jizz18免费视频| 久久亚洲sm情趣捆绑调教| 四虎成人免费观看在线网址| 人成电影网在线观看免费| 久久亚洲精品无码VA大香大香| 国产成人免费片在线视频观看| 国产永久免费高清在线| 亚洲精品无码永久在线观看男男 | 免费国产黄线在线观看| 国产精品免费看久久久香蕉| 亚洲国产美女在线观看 | 日韩在线看片免费人成视频播放| 和老外3p爽粗大免费视频| 亚洲首页国产精品丝袜| 久久久无码精品亚洲日韩软件| 18勿入网站免费永久| 和老外3p爽粗大免费视频| 亚洲经典千人经典日产| 伊人久久综在合线亚洲2019| 亚洲 另类 无码 在线| 国产h肉在线视频免费观看| h片在线观看免费| 亚洲AV香蕉一区区二区三区| 久久久久亚洲av无码专区| 亚洲欧洲国产成人综合在线观看| 国产成人精品久久免费动漫| 久久最新免费视频| 国产精品亚洲AV三区| 亚洲国产成人精品久久| 国产精品亚洲A∨天堂不卡 | 亚洲精品在线不卡| 国产V亚洲V天堂A无码| 四虎永久免费地址在线观看| 国产v精品成人免费视频400条| 日韩免费电影网址| GOGOGO免费观看国语| 男人和女人高潮免费网站|