Java垃圾回收機制以及內存泄露

一、Java的內存泄露介紹java

首先明白一下內存泄露的概念:內存泄露是指程序執行過程動態分配了內存,但是在程序結束的時候這塊內存沒有被釋放,從而致使這塊內存不可用,這就是內存web

泄露,從新啓動計算機可以解決問題,但是有可能再次發生內存泄露,內存泄露與硬件沒有關係,它是軟件設計的缺陷所致使的。算法

Java發生內存泄露的緣由很是明白,就是長聲明週期對象持有短聲明週期對象的引用就很是可能發生內存泄露。雖然短生命週期對象已經再也不需要,但是因爲長生命編程

週期對象在持有它的引用而致使它不能被GC回收,這就是Java內存泄露發生的場景。windows

java內存泄露場景舉例:tomcat

當咱們不斷的向集合類內加入元素,而沒有對應的刪除機制,致使內存一直被佔用,這樣也不必定就會形成內存泄露,當該集合類僅僅是一個局部變網絡

量的時候,當方法運行完成退出的時候,天然會被GC所回收,但是怕的是該集合類是一個全局的屬性(比方類中的靜態變量),那麼會致使該集合類佔用的內存僅僅架構

增不減,這樣就致使了內存的泄露。因此咱們在使用全局性的集合類的時候要注意提供合適的刪除策略或者按期清理策略。ssh

內存泄露可以分爲4類:jvm

(1)常發性內存泄露:發生內存泄露的代碼會屢次被運行到,每次運行的時候就會有一塊內存泄露

(2)偶發性內存泄露:發生內存泄露的代碼僅僅在某些特定環境下才會發生的,常發性與偶發性是相對的,對於特定的環境下,偶發性也就是常發性的。因此,測

試方法和測試環境對檢測內存泄露有是很是重要的。

(3)一次性內存泄露:發生內存泄露的代碼僅僅會被運行一次,也就內存中總有那麼一塊內存是不可用的。

(4)隱式內存泄露    :程序運行過程當中在不斷的分配內存,直到程序結束的時候纔會釋放所有的內存,嚴格的說,這裏並無發生內存泄露,因爲程序終於釋放

了所有申請的內存。但是對於一個server程序來講,每每會連續運行很是多天甚至好幾個月的,這樣早晚會發生內存溢出的狀況。因此,咱們稱這類內存泄露爲隱式

內存泄露。

二、Java內存溢出的問題及解決的方法

JVM管理的內存大體分爲三種不一樣類型的內存區域:

Generation space(永久保存區域)、Heap space(堆區域)、JavaStacks(Java棧)。當中永久保存區域主要存放Class(類)和Meta的信息,Class第一次被Load

的時候被放入PermGenspace區域,Class需要存儲的內容主要包含方法和靜態屬性。堆區域用來存放Class的實例(即對象),對象需要存儲的內容主要是非靜態

屬性。每次用new建立一個對象實例後,對象實例存儲在堆區域中,這部分空間也被jvm的垃圾回收機制管理。而Java棧跟大多數編程語言包含彙編語言的棧功能相

似,主要基本類型變量以及方法的輸入輸出參數。Java程序的每個線程中都有一個獨立的堆棧。easy發生內存溢出問題的內存空間包含:

Permanent Generation space和Heap space。

第一種OutOfMemoryError PermGenspace

發生這樣的問題的原意是程序中使用了大量的jarclass,使java虛擬機裝載類的空間不夠,與PermanentGeneration space有關。解決這類問題有下面兩種辦法:

(1) 添加java虛擬機中的XX:PermSizeXX:MaxPermSize參數的大小,當中XX:PermSize是初始永久保存區域大小,XX:MaxPermSize是最大永久保存區域大

小。如針對tomcat6.0,在catalina.shcatalina.bat文件裏一系列環境變量名說明結束處(大約在70行左右)添加一行:JAVA_OPTS=" -XX:PermSize=64M-

XX:MaxPermSize=128m"假設是windowsserver還可以在系統環境變量中設置。感受用tomcat公佈sprint+struts+hibernate架構的程序時很是easy發生這樣的內存溢出

錯誤。使用上述方法,我成功攻克了部ssh項目的tomcatserver經常宕機的問題。

(2) 清理應用程序中web-inf/lib下的jar,假設tomcat部署了多個應用,很是多應用都使用了一樣的jar,可以將共同的jar移到tomcat共同的lib下,下降類的反覆加

載。這樣的方法是網上部分人推薦的,我沒試過,但感受下降不了太大的空間,最靠譜的仍是第一種方法。

另一種OutOfMemoryError  Javaheap space

發生這樣的問題的解決辦法是java虛擬機建立的對象太多,在進行垃圾回收之間,虛擬機分配的到堆內存空間已經用滿了,與Heapspace有關。解決這類問題有兩種思

路:

(1)檢查程序,看是否有死循環或沒必要要地反覆建立大量對象。找到緣由後,改動程序和算法。

我曾經寫一個使用K-Means文本聚類算法對幾萬條文本記錄(每條記錄的特徵向量大約10來個)進行文本聚類時,由於程序細節上有問題,就致使了Javaheap

 space的內存溢出問題,後來經過改動程序獲得瞭解決

(2)添加Java虛擬機中Xms(初始堆大小)和Xmx(最大堆大小)參數的大小。如:setJAVA_OPTS= -Xms256m -Xmx1024m

三、Java的GC機制

一個優秀的程序猿必須瞭解GC的原理、怎樣有變化GC的性能、怎樣與GC進行有限的交互,因爲一些程序對性能要求較高,好比嵌入式系統、實時系統等,僅僅有

全面提高內存的管理效率,才幹有效提高程序的性能。

GC的基本原理:

Java的內存管理實際上就是對象的管理,包含對象的分配與釋放

對於程序猿來講,分配對象使用newkeyword,釋放對象就是將對象的所有引用賦值爲null,讓程序不能訪問到這個對象,咱們稱之爲‘不可達’狀態,GC負責回收所

有不可達狀態的對象的內存空間。

對於GC來講,當程序猿建立了對象,GC就開始監控這個對象的大小、地址、使用狀況。一般GC採用有向圖的方式來管理堆(heap)中所有對象。經過這樣的方式

來肯定哪些對象是可達的,哪些對象是不可達的,當GC肯定了一些對象是不可達狀態的時候,GC就有責任將這些對象回收。但是,爲了保證GC能夠在不一樣平臺實

現的問題,Java規範對GC的很是多行爲都沒有進行嚴格的規定。好比,對於採用什麼類型的回收算法、何時進行回收等重要問題都沒有明白的規定。所以,不

同的JVM的實現者每每有不一樣的實現算法。

這也給Java程序猿的開發帶來行多不肯定性。本文研究了幾個與GC工做相關的問題,努力下降這樣的不肯定性給Java程序帶來的負面影響。 

增量式GC( Incremental GC )

GC在JVM中通常是由一個或一組進程來實現的,它自己也和用戶程序同樣佔用heap空間,執行時也佔用CPU.當GC進程執行時,應用程序中止執行。

所以,當GC執行時間較長時,用戶能夠感到 Java程序的停頓,另一方面,假設GC執行時間過短,則可能對象回收率過低,這意味着還有很是多應該回收的對象沒

有被回收,仍然佔用大量內存。所以,在設計GC的時候,就必須在停頓時間和回收率之間進行權衡。一個好的GC實現贊成用戶定義本身所需要的設置,好比有些內

存有限有設備,對內存的使用量很敏感,但願GC能夠準確的回收內存,它並不在乎程序速度的放慢。另一些實時網絡遊戲,就不能夠贊成程序有長時間的中

斷。增量式GC就是經過必定的回收算法,把一個長時間的中斷,劃分爲很是多個小的中斷,經過這樣的方式下降GC對用戶程序的影響。儘管,增量式GC在整體性能上

可能不如普通GC的效率高,但是它能夠下降程序的最長停頓時間。Sun JDK提供的HotSpot JVM就能支持增量式GC.HotSpot JVM缺省GC方式爲不使用增量GC,爲

了啓動增量GC,咱們必須在執行Java程序時添加-Xincgc的參數。

HotSpot JVM增量式GC的實現是採用Train GC算法。它的基本想法就是,將堆中的所有對象依照建立和使用狀況進行分組(分層),將使用頻繁高和具備相關性

的對象放在一隊中,隨着程序的執行,不斷對組進行調整。當GC執行時,它老是先回收最老的(近期很是少訪問的)的對象,假設整組都爲可回收對象,GC將整組回

收。這樣,每次GC執行僅僅回收必定比例的不可達對象,保證程序的順暢執行。

具體解釋finalize函數

finalize是位於Object類的一個方法,該方法的訪問修飾符爲protected,由於所有類爲Object的子類,所以用戶類很是easy訪問到這種方法。由於,finalize函

數沒有本身主動實現鏈式調用,咱們必須手動的實現,所以finalize函數的最後一個語句通常是super.finalize()

經過這樣的方式,咱們可以實現從下到上實現finalize的調用,即先釋放本身的資源,而後再釋放父類的資源。依據Java語言規範,JVM保證調用finalize函數之

前,這個對象是不可達的,但是JVM不保證這個函數必定會被調用。另外,規範還保證finalize函數最多執行一次。很是多Java剛開始學習的人會以爲這種方法相似與C++中

的析構函數,將很是多對象、資源的釋放都放在這一函數裏面。事實上,這不是一種很是好的方式。緣由有三:

其一,GC爲了能夠支持finalize函數,要對覆蓋這個函數的對象做很是多附加的工做。

其二,在finalize執行完畢以後,該對象可能變成可達的,GC還要再檢查一次該對象是不是可達的。所以,使用 finalize會減小GC的執行性能。

其三,由於GC調用finalize的時間是不肯定的,所以經過這樣的方式釋放資源也是不肯定的。一般,finalize用於一些不easy控制、並且很重要資源的釋放,例

如一些I/O的操做,數據的鏈接。這些資源的釋放對整個應用程序是很關鍵的。在這樣的狀況下,程序猿應該以經過程序自己管理(包含釋放)這些資源爲主,以

finalize函數釋放資源方式爲輔,造成一種雙保險的管理機制,而不該該只依靠finalize來釋放資源。

相關文章
相關標籤/搜索