[jvm解析系列][一]Java內存區域分配和內存溢出異常OOM

學過操作系統的同學應該比較清楚,一個操作系統必須要有完善的內存管理系統(頁/段式的管理),相應的jvm全稱java虛擬機應該也有類似的一種管理內存的方式,這種方式是建立在真實的操作系統內存管理方式之上的,他把內存分配成了不同的區域,形成了java內存模型。

那麼,對於其他博客講解這種題目要先拋一個圖解出來,我並不想這樣。因爲這種模型的出現肯定是要解決問題的,我們需要順延着前人設計jvm內存模型的腳步,看看到底爲什麼要這樣設計,才能更好的理解它。

首先,我們思考一下,java需要什麼區域呢?

1、必不可少的,一個程序計數器,用來記錄當前程序運行的字節碼指令,jvm使用了棧而不是寄存器結構(往後看相信你會理解的)。對於線程來講,每個線程肯定是需要記錄自己的執行位置,所以程序計數器是線程私有的

2、既然有了程序計數器我們還需要一個區域來存放當前運行的程序,那就是Java虛擬機棧,每一個方法的執行就會添加一個棧幀在虛擬機棧裏(如果想要詳細的瞭解棧幀請關注博客,隨着系列的加深,我會一個個講解)。對於線程來講虛擬機棧同樣的是線程私有。

3、一樣的常用jvm的都知道還有一種jni的調用,爲了這種方法的運行,設計了一種跟java虛擬機棧的雙胞胎區域叫本地方法棧,除了運行native方法外其他的幾乎一致。(在有的虛擬機裏不區分native和java)

4、大家都知道java是面向對象的,既然方法都出來了,那麼類存儲在那呢?沒錯,jvm也分化出來了這樣的一個區域叫做方法區(也稱爲永久代,因爲這個區域很長時間不會發生gc)。這個區域裏存儲了虛擬機家在的所有的類信息,常量和靜態變量等。(內含一個運行時常量池主要用於存放編譯器生成的字面量和符號引用)他是線程公有的。

5、如果類都有了,那麼對象呢?沒錯這就是jvm的重頭戲,很多工作都在這個地方完成,他就是傳說中的java堆,在有的地方甚至把jvm籠統的分爲堆棧,可見java堆的重要性。java堆裏面存放了幾乎所有的對象實例,在java虛擬機規範中這樣說:所有的對象實例和數組都要在堆上分配。隨着JIT編譯器等的發展也不那麼絕對了。(java堆仍然很複雜,後期單開一篇博客詳解)。它肯定是線程公有的。

6、到這裏,好像java所有的需要都被分配了,以前確實是的,但是在JDK1.4之後,java引入了nio技術(不在本章範圍內講解),這就要求需要分配一個新的區域,沒錯,他叫直接內存。值得一提的是,這一區域並不是jvm的規範中的一部分,也不是虛擬機運行時數據區的一部分,但是他會引起OOM。

那麼至此所有的內存區域分配完畢,配圖如下幫助理解,相信你已經差不多的瞭解和理解了jvm分配的方式和目的了。

內存區域分配配圖


上節說到~jvm的內存分配,所以對應的應該有6種溢出方式,實際上程序計數器一般不會溢出,我又把兩個方法棧合在一起講,所以溢出的可能性有4種

嘿嘿嘿嘿,我要去考試了,等我回來再補吧~

回來了繼續補

1、Java堆溢出

java堆溢出一般都是因爲在for或者遞歸裏調用了太多次new對象的操作,並且可以被GCROOT查找到不能GC,而導致java堆內存不足報錯,報錯一般如下:

OutOfMemoryError:Java heap spce

2、虛擬機棧和本地方法棧溢出

虛擬機棧和本地方法溢出一般也是陷入了無限循環裏並且在無限循環裏一直在調用一個方法,因爲一般情況下棧深能有1000~2000,報錯一般都是StackOverflowError,OOM反而不常見,在處理這種異常時很棘手,需要減少其他區域的內存,因爲這塊內存是用總內存減去堆和方法區得到的。報錯如下:

StackOverflowError.

3、方法區和運行時常量池溢出

這個區域想要溢出單單使用創建類的方法和常量是困難的,所以可以使用String.intern()方法直接將字符串加入到常量池。報錯一般如下

OOM:PermGen space

4、本機直接內存溢出

由於上文知道直接內存一般用於NIO操作,所以使用了native方法而且因爲這個區域的特殊性Heap dump並沒有明顯的報錯。錯誤信息如下:

OutOfMemory。

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