納尼,Java 存在內存泄泄泄泄泄泄漏嗎?

01. 怎麼回事?

納尼,Java 不是自動管理內存嗎?怎麼可能會出現內存泄泄泄泄泄泄漏!html

Java 最牛逼的一個特性就是垃圾回收機制,不用像 C++ 須要手動管理內存,因此做爲 Java 程序員很幸福,只管 New New New 便可,反正 Java 會自動回收過時的對象。。。java

那麼 Java 都自動管理內存了,那怎麼會出現內存泄漏,難道 Jvm 有 bug? 不要急,且聽我慢慢道來。。git

02. 怎麼判斷能夠被回收

先了解一下 Jvm 是怎麼判斷一個對象能夠被回收。通常有兩種方式,一種是引用計數法,一種是可達性分析。程序員

引用計數法:每一個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數爲0時能夠回收。面試

這個辦法看起來挺簡單的,可是若是出現 A 引用了 B,B 又引用了 A,這時候就算他們都再也不使用了,但由於相互引用 計算器=1 永遠沒法被回收。工具

此方法簡單,沒法解決對象相互循環引用的問題。3d

可達性分析(Reachability Analysis):從 GC Roots 開始向下搜索,搜索所走過的路徑稱爲引用鏈。當一個對象到 GC Roots 沒有任何引用鏈相連時,則證實此對象是不可用的,那麼虛擬機就判斷是可回收對象。code

可達性分析能夠解決循環引用的問題。htm

那麼 gc roots 對象是哪些呢對象

  • 虛擬機棧中引用的對象
  • 方法區中類靜態屬性引用的對象
  • 方法區中常量引用的對象
  • 本地方法棧中JNI[即通常說的Native]引用的對象

目前主流的虛擬機中大多使用可達性分析的方式來斷定對象是否可被 GC 回收。

03. 什麼狀況下會出現內存泄漏

既然可達性分析好像已經很牛逼的樣子了,怎麼可能還會出現內存泄漏呢,那咱們再來看一下內存泄漏的定義。

內存泄露就是指一個再也不被程序使用的對象或變量一直被佔據在內存中。

有可能此對象已經不使用了,可是還有其它對象保持着此對象的引用,就會致使 GC 不能回收此對象,這種狀況下就會出現內存泄漏。

寫一個程序讓出現內存泄漏

①長生命週期的對象持有短生命週期對象的引用就極可能發生內存泄露,儘管短生命週期對象已經再也不須要,可是由於長生命週期對象持有它的引用而致使不能被回收。

public class Simple {
    Object object;
    public void method1(){
        object = new Object();
        //...其餘代碼
    }
}

這裏的 object 實例,其實咱們指望它只做用於 method1() 方法中,且其餘地方不會再用到它,可是,當method1()方法執行完成後,object 對象所分配的內存不會立刻被認爲是能夠被釋放的對象,只有在 Simple 類建立的對象被釋放後纔會被釋放,嚴格的說,這就是一種內存泄露。

解決方法就是將 object 做爲 method1() 方法中的局部變量。

public class Simple {
    Object object;
    public void method1(){
        object = new Object();
        //...其餘代碼
        object = null;
    }
}

固然你們有可能會想就這一個方法也不會有多大影響,但若是在某些項目中,一個方法在一分鐘以內調用上萬次的時候,就會出現很明顯的內存泄漏現象。

②集合中的內存泄漏,好比 HashMap、ArrayList 等,這些對象常常會發生內存泄露。好比當它們被聲明爲靜態對象時,它們的生命週期會跟應用程序的生命週期同樣長,很容易形成內存不足。

下面給出了一個關於集合內存泄露的例子。

Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
    Object o=new Object();
    v.add(o);
    o=null;
}
//此時,全部的Object對象都沒有被釋放,由於變量v引用這些對象。

在這個例子中,咱們循環申請 Object 對象,並將所申請的對象放入一個 Vector 中,若是咱們僅僅釋放引用自己,那麼 Vector 仍然引用該對象,因此這個對象對 GC 來講是不可回收的。

所以,若是對象加入到 Vector 後,還必須從 Vector 中刪除,最簡單的方法就是將 Vector 對象設置爲 null。

以上兩種是最多見的內存泄漏案例。固然還有一些內存泄漏的例子,這裏就再也不一一例舉了,感興趣的同窗能夠在網上找找資料。

04. 內存泄漏和內存溢出

不少同窗老是搞不清楚,內存泄漏和內存溢出的區別,它倆是兩個徹底不一樣的概念, 它們之間存在一些關聯。

內存溢出 out of memory,是指程序在申請內存時,沒有足夠的內存空間供其使用,出現 out of memory;

內存泄露 memory leak,是指程序在申請內存後,沒法釋放已申請的內存空間,一次內存泄露危害能夠忽略,但內存泄露堆積後果很嚴重,不管多少內存,早晚會被佔光。

因此內存泄漏可能會致使內存溢出,但內存溢出並不徹底都是由於內存泄漏,也有可能使用了太多的大對象致使。

05. 如何檢測內存泄漏

最後一個重要的問題,就是如何檢測 Java 的內存泄漏。目前,咱們一般使用一些工具來檢查 Java 程序的內存泄漏問題。

市場上已有幾種專業檢查 Java 內存泄漏的工具,它們的基本工做原理大同小異,都是經過監測 Java 程序運行時,全部對象的申請、釋放等動做,將內存管理的全部信息進行統計、分析、可視化。開發人員將根據這些信息判斷程序是否有內存泄漏問題。

這些工具包括 Plumbr 、Eclipse Memory Analyzer、JProbe Profiler、JVisualVM 等。

06. 最後

以上內容實際上是我曾經常常面試的內容之一,經過一系列的問題考察 Java 程序員對 Jvm 的理解。

好比我一般會問面試者,Java 中存在內存泄漏嗎?大部分人都會回答存在,接着我會問若是讓你寫一個程序讓內存泄漏,你會怎麼寫?大部分程序員就回答不上來了。

若是面試者能夠回答上面的問題,我會接着和麪試者聊聊,內存泄漏和內存溢出他們之間是否存在聯繫 、以及在平常工做中如何避免寫出內存泄漏的代碼 、若是生產出現 Jvm 相關問題時,排查問題的思路和步驟等等。

這些問題在個人博客中都有答案,早些年寫了一系列關於 Jvm 的文章,你們若是感興趣的話接下來繼續去閱讀,http://www.ityouknow.com/java.html

若是你們以爲在手機上看着更方便,能夠關注:Java 極客技術公號,已經輸出了一些 JVM 文章,我博客中的 Jvm 系列文章也都會推送到這個公號中。

關注一下又不會懷孕

參考出處:

https://lovoedu.gitee.io/javablog/2017/08/27/20170827/
https://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/index.html

相關文章
相關標籤/搜索