Java內存泄露原因以及解決方法

原文:https://blog.csdn.net/wtt945482445/article/details/52483944 

首先,需要了解 JAVA 的內存分配策略,以及堆棧的區別

Java 內存分配策略

  • Java 程序運行時的內存分配策略有三種,分別是靜態分配,棧式分配,和堆式分配,對應的,三種存儲策略使用的內存空間主要分別是靜態存儲區(也稱方法區)、棧區和堆區。
  • 靜態存儲區(方法區):主要存放靜態數據、全局 static 數據和常量。這塊內存在程序編譯時就已經分配好,並且在程序整個運行期間都存在。
  • 棧區 :當方法被執行時,方法體內的局部變量(其中包括基礎數據類型、對象的引用)都在棧上創建,並在方法執行結束時這些局部變量所持有的內存將會自動被釋放。因爲棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
  • 堆區 : 又稱動態內存分配,通常就是指在程序運行時直接 new 出來的內存,也就是對象的實例。這部分內存在不使用時將會由 Java 垃圾回收器來負責回收。

棧與堆的區別:

在方法體內定義的(局部變量)一些基本類型的變量和對象的引用變量都是在方法的棧內存中分配的。當在一段方法塊中定義一個變量時,Java 就會在棧中爲該變量分配內存空間,當超過該變量的作用域後,該變量也就無效了,分配給它的內存空間也將被釋放掉,該內存空間可以被重新使用。

堆內存用來存放所有由 new 創建的對象(包括該對象其中的所有成員變量)和數組。在堆中分配的內存,將由 Java 垃圾回收器來自動管理。在堆中產生了一個數組或者對象後,還可以在棧中定義一個特殊的變量,這個變量的取值等於數組或者對象在堆內存中的首地址,這個特殊的變量就是我們上面說的引用變量。我們可以通過這個引用變量來訪問堆中的對象或者數組。
 

總結:

局部變量的基本數據類型和引用存儲於棧中,引用的對象實體存儲於堆中。—— 因爲它們屬於方法中的變量,生命週期隨方法而結束。

成員變量全部存儲與堆中(包括基本數據類型,引用和引用的對象實體)—— 因爲它們屬於類,類對象終究是要被new出來使用的。

瞭解了 Java 的內存分配之後,我們再來看看 Java 是怎麼管理內存的。
 

JAVA中的內存泄露

1.Java內存回收機制

不論哪種語言的內存分配方式,都需要返回所分配內存的真實地址,也就是返回一個指針到內存塊的首地址。Java中對象是採用new或者反射的方法創建的,這些對象的創建都是在堆(Heap)中分配的,所有對象的回收都是由Java虛擬機通過垃圾回收機制完成的。GC爲了能夠正確釋放對象,會監控每個對象的運行狀況,對他們的申請、引用、被引用、賦值等狀況進行監控,Java會使用有向圖的方法進行管理內存,實時監控對象是否可以達到,如果不可到達,則就將其回收,這樣也可以消除引用循環的問題。在Java語言中,判斷一個內存空間是否符合垃圾收集標準有兩個:一個是給對象賦予了空值null,以下再沒有調用過,另一個是給對象賦予了新值,這樣重新分配了內存空間。

2.Java內存泄漏引起的原因

內存泄漏是指無用對象(不再使用的對象)持續佔有內存或無用對象的內存得不到及時釋放,從而造成內存空間的浪費稱爲內存泄漏。內存泄露有時不嚴重且不易察覺,這樣開發者就不知道存在內存泄露,但有時也會很嚴重,會提示你Out of memory。j

Java內存泄漏的根本原因是什麼呢?長生命週期的對象持有短生命週期對象的引用就很可能發生內存泄漏,儘管短生命週期對象已經不再需要,但是因爲長生命週期持有它的引用而導致不能被回收,這就是Java中內存泄漏的發生場景。具體主要有如下幾大類:
    1)靜態集合類引起內存泄漏:

    2)當集合裏面的對象屬性被修改後,再調用remove()方法時不起作用。JDK1.8 貌似修正了引用對象修改參數,導致hashCode變更的問題。

    3)監聽器 Listener 各種連接 Connection

    4)內部類和外部模塊的引用(儘量使用靜態內部類)

    5)單例模式(靜態類持有引用,導致對象不可回收)

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章