JVM內存管理

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

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

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

【內存空間的劃分】

Sun JDK實現時遵照JVM規範,將內存空間劃分爲方法區、堆、JVM方法棧、本地方法棧及pc寄存器。

* 方法區:

存放要加載的類or接口的信息(名稱、修飾符等)、類的static變量、final常量、Field信息、方法信息(元數據)。通過Class對象獲取的相關數據就來自該區域。 --見 Class類

Sun JDK中這塊區域對應Permanet Generation(持久代),默認最小值爲16MB,最大值爲64MB,可通過-XX:PermSize-XX:MaxPermSize來設置最小值和最大值。

* 堆(Heap Memory)

存放對象實例及數組值,Heap中對象所佔用的內存由GC進行回收,在32位系統上最大爲2G,64位系統上無限制。可通過-Xms-Xmx控制,-Xms爲JVM啓動時申請的最小Heap內存,-Xmx爲JVM可申請的最大Heap內存。

* 方法棧

每個線程均會創建PC寄存器和方法棧。方法棧中有棧幀。方法棧爲線程私有。當方法運行完畢,該方法對應的棧幀所佔用的空間自動釋放。

--想到 ThreadLocal

方法棧空間不足時,拋出StackOverflowError錯誤(如不合理的遞歸調用),在Sun JDK中可通過-Xss設置大小。 

 

【Heap Memory詳解】


{    [  (Eden)(S0)(S1)  ]     [ (          ) ]    }

    |----New Gen------|    |-Old Gen--|

|---------------heap-----------------------|

-New Generation:新生代

大多數情況下java程序中新建的對象都從新生代分配內存,新生代由Eden Space和兩塊相同大小的Survivor Space構成。可通過-Xmn指定新生代的大小。

  -Eden Space:創建對象,依據的是方法區中存放的類的元數據。

  -S0:當Eden Space空間用完時,JVM的垃圾回收器對其進行回收,將Eden中不被其他對象使用的對象進行銷燬,同時將Eden中還被其他對象引用的對象移到S0區,如果S0區也沒有空間了則移動到S1區。

  -S1

-Old Generation:舊生代

存放新生代中經過多次垃圾回收仍然存活的對象,例如緩存對象。舊生代佔用大小爲-Xmx值減-Xmn對應的值。


------Shallow Heap


java.lang.OutOfMemoryError: GC overhead limit exceeded

這個是JDK6新添的錯誤類型。是發生在GC佔用大量時間而很少的堆被恢復的時候發生的,是一種保護機制。解決方案是,關閉該功能,使用 -XX:-UseGCOverheadLimit

Sun官方解釋:

The parallel / concurrent collector will throw an OutOfMemoryError if too much time is being spent in garbage collection: if more than 98% of the total time is spent in garbage collection and less than 2% of the heap is recovered, an OutOfMemoryError will be thrown. This feature is designed to prevent applications from running for an extended period of time while making little or no progress because the heap is too small. If necessary, this feature can be disabled by adding the option -XX:-UseGCOverheadLimit to the command line

如何避免:查看是否有使用大內存的代碼或死循環。



System.gc() 與 finalize()】

java.lang.System.gc()

java.lang.Runtime.getRuntime().gc()

java.lang.Object.finalize()

一個題目:
11. rbo = new ReallyBigObject();
12. // more code here
13. rbo = null;
14.
Which statement should be placed at line 14 to suggest that the virtual
machine expend effort toward recycling the memory used by the
object rbo?
A. System.gc();
B. Runtime.getRuntaime().gc();

C. System.freeMemory();
D. Runtime.getRuntime().growHeap();
E. Runtime.getRuntime().freeMemory();


gc()有何用?

  調用 gc 方法暗示着 Java 虛擬機做了一些努力來回收未用對象,以便能夠快速地重用這些對象當前佔用的內存。當控制權從方法調用中返回時,虛擬機已經盡最大努力從所有丟棄的對象中回收了空間。 
調用 System.gc() 等效於調用Runtime.getRuntime().gc()


finalize()有何用?

  gc 只能清除在堆上分配的內存(純java語言的所有對象都在堆上使用new分配內存),而不能清除棧上分配的內存(當使用JNI技術時,可能會在棧上分配內存,例如java調用c程序,而該c程序使用malloc分配內存時)。因此,如果某些對象被分配了棧上的內存區域,那gc就管不着了,對棧上的對象進行內存回收就要靠finalize()。
舉個例子來說,當java 調用非java方法時(這種方法可能是c或是c++的),在非java代碼內部也許調用了c的malloc()函數來分配內存,而且除非調用那個了 free() 否則不會釋放內存(因爲free()是c的函數),這個時候要進行釋放內存的工作,gc是不起作用的,因而需要在finalize()內部的一個固有方法調用free()。
finalize的工作原理應該是這樣的:一旦垃圾收集器準備好釋放對象佔用的存儲空間,它首先調用finalize(),而且只有在下一次垃圾收集過程中,纔會真正回收對象的內存.所以如果使用finalize(),就可以在垃圾收集期間進行一些重要的清除或清掃工作.


finalize()在什麼時候被調用?
有三種情況
1.所有對象被Garbage Collection時自動調用,比如運行System.gc()後.
2.程序退出時爲每個對象調用一次finalize方法。
3.顯式的調用finalize方法


除此以外,正常情況下,當某個對象被系統收集爲無用信息的時候,finalize()將被自動調用。但是jvm不保證finalize()一定被調用,也就是說,finalize()的調用是不確定的,這也就是爲什麼sun不提倡使用finalize()的原因. 簡單來講,finalize()是在對象被GC回收前會調用的方法,而System.gc()建議而非強制GC開始回收工作,具體執行要看GC的執行策略。

調用了 System.gc() 之後,java 在內存回收過程中就會調用那些要被回收的對象的 finalize() 方法。


【待看】

1.Calling System.gc() is a bad idea

2.System.gc() in Java

3.System.GC 算法


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