Java內存泄露和內存溢出詳解 .

 第一部分 概念

  衆所周知,java中的內存由java虛擬機自己去管理的,他不像C++需要自己去釋放。籠統地去講,java的內存分配分爲兩個部分,一個是數據堆,一個是棧。程序在運行的時候一般分配數據堆,把局部的臨時的變量都放進去,生命週期和進程有關係。但是如果程序員聲明瞭static的變量,就直接在棧中運行的,進程銷燬了,不一定會銷燬static變量。

  另外爲了保證java內存不會溢出,java中有垃圾回收機制。 System.gc()即垃圾收集機制是指jvm用於釋放那些不再使用的對象所佔用的內存。java語言並不要求jvm有gc,也沒有規定gc如何工作。垃圾收集的目的在於清除不再使用的對象。gc通過確定對象是否被活動對象引用來確定是否收集該對象。

  而其中,內存溢出就是你要求分配的java虛擬機內存超出了系統能給你的,系統不能滿足需求,於是產生溢出。

  內存泄漏是指你向系統申請分配內存進行使用(new),可是使用完了以後卻不歸還(delete),結果你申請到的那塊內存你自己也不能再訪問,該塊已分配出來的內存也無法再使用,隨着服務器內存的不斷消耗,而無法使用的內存越來越多,系統也不能再次將它分配給需要的程序,產生泄露。一直下去,程序也逐漸無內存使用,就會溢出。

  第二部分 原理

  JAVA垃圾回收及對內存區劃分

  在Java虛擬機規範中,提及瞭如下幾種類型的內存空間:

  ◇ 棧內存(Stack):每個線程私有的。

  ◇ 堆內存(Heap):所有線程公用的。

  ◇ 方法區(Method Area):有點像以前常說的“進程代碼段”,這裏面存放了每個加載類的反射信息、類函數的代碼、編譯時常量等信息。

  ◇ 原生方法棧(Native Method Stack):主要用於JNI中的原生代碼,平時很少涉及。

  而Java的使用的是堆內存,java堆是一個運行時數據區,類的實例(對象)從中分配空間。Java虛擬機(JVM)的堆中儲存着正在運行的應用程序所建立的所有對象,“垃圾回收”也是主要是和堆內存(Heap)有關。

  垃圾回收的概念就是JAVA虛擬機(JVM)回收那些不再被引用的對象內存的過程。一般我們認爲正在被引用的對象狀態爲“alive”,而沒有被應用或者取不到引用屬性的對象狀態爲“dead”。垃圾回收是一個釋放處於”dead”狀態的對象的內存的過程。而垃圾回收的規則和算法被動態的作用於應用運行當中,自動回收。

  JVM的垃圾回收器採用的是一種分代(generational )回收策略,用較高的頻率對年輕的對象(young generation)進行掃描和回收,這種叫做minor collection,而對老對象(old generation)的檢查回收頻率要低很多,稱爲major collection。這樣就不需要每次GC都將內存中所有對象都檢查一遍,這種策略有利於實時觀察和回收。

  (Sun JVM 1.3 有兩種最基本的內存收集方式:一種稱爲copying或scavenge,將所有仍然生存的對象搬到另外一塊內存後,整塊內存就可回收。這種方法有效率,但需要有一定的空閒內存,拷貝也有開銷。這種方法用於minor collection。另外一種稱爲mark-compact,將活着的對象標記出來,然後搬遷到一起連成大塊的內存,其他內存就可以回收了。這種方法不需要佔用額外的空間,但速度相對慢一些。這種方法用於major collection. )

  一些對象被創建出來只是擁有短暫的生命週期,比如 iterators 和本地變量。另外一些對象被創建是擁有很長的生命週期,比如持久化對象等。

  垃圾回收器的分代策略是把內存區劃分爲幾個代,然後爲每個代分配一到多個內存區塊。當其中一個代用完了分配給他的內存後,JVM會在分配的內存區內執行一個局部的GC(也可以叫minor collection)操作,爲了回收處於“dead”狀態的對象所佔用的內存。局部GC通常要比Full GC快很多。

JVM定義了兩個代,年輕代(yong generation)(有時稱爲“nursery”託兒所)和老年代(old generation)。年輕代包括 “Eden space(伊甸園)”和兩個“survivor spaces”。虛擬內存初始化的時候會把所有對象都分配到 Eden space,並且大部分對象也會在該區域被釋放。 當進行 minor GC的時候,VM會把剩下的沒有釋放的對象從Eden space移動到其中一個survivor spaces當中。此外,VM也會把那些長期存活在survivor spaces 裏的對象移動到 老生代的“tenured” space中。當 tenured generation 被填滿後,就會產生Full GC,Full GC會相對比較慢因爲回收的內容包括了所有的 live狀態的對象。pemanet generation這個代包括了所有java虛擬機自身使用的相對比較穩定的數據對象,比如類和對象方法等。

  關於代的劃分,可以從下圖中獲得一個概況:





      第三部分 總結

  內存溢出主要是由於代碼編寫時對某些方法、類應用不合理,或者沒有預估到臨時對象會佔用很大內存量,或者把過多的數據放入JVM緩存,或者性能壓力大導致消息堆積而佔用內存,以至於在性能測試時,生成龐大數量的臨時對象,GC時沒有做出有效回收甚至根本就不能回收,造成內存空間不足,內存溢出。

  如果編碼之前,對內存使用量進行預估,對放在內存中的數據進行評估,保證有用的信息儘快釋放,無用的信息能夠被GC回收,這樣在一定程度上是可以避免內存溢出問題的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章