天天看點

jvm記憶體模型總結JVM記憶體模型

JVM記憶體模型

之前看過很多關于jvm記憶體模型的文章,什麼有堆棧,方法區,程式計數器等劃分,也有文章說主存和工作記憶體的模型。

這裡我覺得無論是jvm記憶體區域的劃分,還是主存工作記憶體的使用,都是屬于jvm記憶體模型内的知識。基于自己的了解,簡單總結一下,單純隻是個人總結,不一定是正确的,假如看到有錯誤的地方可以幫忙指出。

一、jvm運作期的記憶體區域劃分:

jvm記憶體模型總結JVM記憶體模型

上述圖可以比較直覺的看出jvm運作期,哪些記憶體區域屬于共享的,哪些屬于線程私有的。下面簡單的介紹每個記憶體區域:

1.線程私有的記憶體區域:

(1)java 棧(jvm stack)

主要存放一個個棧幀,每個棧幀對應着線程執行每個方法相關的資料(局部變量表,操作棧,動态連結,方法出口等資訊),每一個方法執行完了,對應的棧幀就會從java棧出棧。當棧的深度達到jvm所允許的最大深度,就會抛出

stackoverflowerror

的錯誤,當擴充無法申請到足夠的記憶體則會抛出

outofmemoryerror

的錯誤。

(2)程式計數器(PC register)

很小的記憶體空間,因為jvm底層執行程式是執行位元組碼指令,而程式計數器就記錄着目前指令的執行位置。随着指令執行而變化,進而擷取下一個需要執行的指令(可實作分支,循環,跳轉,異常)。

(3)本地方法棧(native method stack)

與java棧存儲内容很相似,隻是java棧是服務于java方法(位元組碼),而本地方法棧是服務于native方法。同樣也會抛出上面提到的異常錯誤。

2.線程共享的記憶體區域:

(1)堆(heap)

這一塊主要是存放對象資料,是jvm記憶體中最大的一塊。包含了新生代和年老代,也是GC(垃圾回收)主要收集的區域。(GC算法會在後面的文章裡提到),當記憶體申請超過了jvm定義的記憶體大小,則會報outofmemory的錯誤。

(2.1)本地方法區(Method Area)

主要存放的是類資訊,常量,靜态變量等資料,在jdk1.8之前,hotspot裡是将方法區當做永久代來使用,之後廢除了永久代,這裡會在另外一篇文章裡解釋。

(2.2)運作時常量池(Runtime Constant Pool)

屬于方法區的一部分,用于存放編譯期生成的各種字面量和符号引用,如字元串,final變量,類名和方法名常量等(這裡需要注意常量池的常量的存儲大小)

二、主存和工作記憶體:

與記憶體區域劃分不在同一個層面上,jvm中的所有變量都是存放在主存中,所有線程在擷取這些變量的時候,首先需要将變量拷貝一份到自己的工作記憶體中,然後再在工作記憶體對這些變量進行操作。不同線程無法通路彼此的工作記憶體中的變量。

jvm記憶體模型總結JVM記憶體模型

是以假如A線程想要修改某個變量test1,首先将線程的工作記憶體中的變量指派,然後再将變量傳遞到主存上去。

這樣的模型,假如是線程操作私有的變量是沒有問題的,假如是線程操作共享的變量,可能就會引起操作結果有誤。比如當線程A将共享變量test2拷貝到A的工作記憶體,這個時候B線程也拷貝了test2到B的工作記憶體,當A操作完test2經過flush将值刷到主存後,B線程操作完test2同步到主存的時候,會将A的操作覆寫掉。

這樣的問題就是同步問題。同步問題可以通過synchronized鎖或者volatile來實作。(如何實作同步将會在另外的文章裡講述)

後續有補充,會持續更新。