(JVM1)Java內存區域與內存溢出異常

寫這篇文章,主要是爲了記錄自己複習java虛擬機的一些心得總結筆記,僅供分享參考學習,如有不對的地方,敬請指正。

首先,我們要知道,爲什麼我們要學JVM的一些知識,原因在於,java的內存管理是交給虛擬機進行的,不像C或者C++是可以自己管理的,所以,只有瞭解了虛擬機是怎樣使用內存的,才能在遇到內存溢出或者內存泄露問題的時候,更好的排查解決問題,這就是學習JVM內存知識的意義。

java的jvm包涵了5個運行時數據區域,即方法區(Method Area)虛擬機棧(VM Stack),本地方法棧(Native Method Stack)堆(Heap)程序計數器(Program Counter Register)


下面,將一個個介紹。

1、程序計數器

程序計數器,佔用的內存空間比較小,它是當前線程所執行的字節碼(java編譯後就是字節碼)的行號指示器。也就是當前字節碼解釋器通過計數器得知下面應該執行那條字節碼指令。例如分支、異常處理等。

Java虛擬機的多線程機制是通過輪流分配執行時間的方式實現,所以,同一時間,一個內核只會執行一條線程中的指令,那麼,爲了能夠在線程切換的時候,能保證知道該線程該執行那條指令,所以每個線程都有一個獨立的程序計數器。每個線程的程序計數器互不影響,這就叫線程私有,也就是每個計數器的內存區域都屬於一個線程私有

如果線程在執行一個java方法,則計數器記錄的是正在執行的字節碼指令地址。如果是Native方法,則計數器值爲空(Undefined)。

程序計數器內存區域不會有也是唯一一個沒有OutOfMemoryError的區域

2、java虛擬機棧

與程序計數器一樣,java虛擬機棧也是線程私有的,它的生命週期與線程相同。虛擬機棧,描述的是Java方法執行的內存模型:每個java方法在 執行時都會創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等。一個方法開始執行對應一個棧幀在虛擬機棧中的入棧,執行完成對應出棧。

局部變量表存放編譯期可知的各種基本數據類型、對象的引用(對應起始地址的引用指針)以及returnAddress(這個其實我也不是很理解)

局部變量表在編譯器就已經確定了大小並完成分配。

java虛擬機棧有兩種異常狀況:(1)StackOverFlowError異常,如果線程請求的棧深度大於虛擬機所允許的深度則拋出(當前大部分JVM都允許動態擴展,當然,也可以設置固定值)。(2)OutOfMemoryError異常,如果擴展時沒法申請到足夠的內存,就會拋出該異常。


3、本地方法棧

這個區域可以理解爲跟java虛擬機棧一樣,只是他是爲Native方法服務的,java虛擬機棧是爲java方法服務的。

這個區域也會拋出StackOverFlowError和OutOfMemoryError異常。

有的虛擬機把虛擬機棧和本地方法棧合併(如Sun HotSpot虛擬機)。


4、Java堆

java堆是JVM內存管理中最大的一塊內存,它被所有線程共享,在虛擬機啓動時創建。

java中所有的對象實例和數組都在堆上分配。Java虛擬機規範中的原文:The heap is the runtime data area from which memory for  all class instances and arrays is allocated.

堆,是垃圾收集器管理的主要區域,也叫GC堆,垃圾堆,呵呵。

在內存分配的角度看,由於現在收集器基本都採用分代收集算法,所以java堆中還可以細分爲新生代和老年代,再細分可以分爲Eden空間、From Survivor空間和To Survivor空間。

Java堆可以處於物理上不連續的內存空間中,只要邏輯上連續即可。它可以是固定大小,也可以動態擴展(-Xmx最大,-Xms最小)。

我們知道,每次生成一個對象實例都需要申請內存,如果內存不夠,則會拋出OutOfMemoryError異常。


5、方法區

方法區也是所有線程共享的內存區域。它用於存儲已經被JVM加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。在JVM規範中,它被描述爲堆的一個邏輯部分,但是它卻又有一個別名,叫Non-Heap(非堆),目的可能是用來與java堆區分開來。它也被成爲“永久代“。永久代有-XX:MaxPermSize的上限。在這裏,需要指出String.intern方法,說明如下:當一個String實例str調用intern()方法時,Java查找常量池中是否有相同Unicode的字符串常量,如果有,則返回其的引用,如果沒有,則在常量池中增加一個Unicode等於str的字符串並返回它的引用。所以,String.intern有可能讓永久代產生OutOfMemoryError異常。

方法區不需要連續的內存,可以選擇固定大小也可以動態擴展,更可以選擇不實現垃圾收集。垃圾收集在方法區比較少出現,但不代表不出現。該區域的垃圾收集主要針對常量的回收和對類型的卸載。這個區域的垃圾回收是必要卻又不如人意的,尤其是對類型的卸載。

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