1. 程式人生 > >Java內存數據模型

Java內存數據模型

局部變量表 ole flow 記錄 本地線程 sta 實現 啟動 運行時

本篇文章帶來的是對Java內存數據模型的介紹,這對於我們深入理解Jvm虛擬機工作的原理和Java內存的劃分大有裨益,好了,為了讓我們理解的更為深刻,我們將會加入圖片輔助的方法去理解。

本篇博文的目錄:

一:Java內存數據模型的介紹

二:線程私有內存

三:程序計數器

四:Java虛擬機棧

五:本地方法棧

六:Java堆

七:方法區

八:運行時常量池

九:總結

一:java內存數據模型的介紹

java程序在運行的時候會在內存中開辟不同的空間用以管理不用的內存區域,每個區域都有自己的功能,創建和銷毀時間,有的區域會隨著虛擬機的啟動而創建,而有的區域會隨著用戶線程的創建而銷毀。按照空間,分為如下不同的空間,接下來我們將按照不同的區域進行學習,學習它不同區域的功能和用法

技術分享

圖 1-1 :java內存數據模型

二:線程私有內存

我們先來看一下多線程運行原理:其實cpu在運行的時候每次在同一特定的時間點只能運行一個線程(單核的情況下),只不過它切換的速度非常之快,讓我們覺的它是在進行多線程運行,本質上它還是單一運行的。所以這就會必然引出一個問題:cpu切換線程如何保證它自身的運行不受其他的線程影響,保證每個線程都是獨立不受外部侵擾的,這就產生了線程私有內存這個概念,主要是維持線程的安全、穩定、高效的運轉。關於這一點也很好理解,比如我們去一個快速餐廳吃飯,我們點好了菜,會產生菜單小票,這個小票上面有編號,就是每個線程的“私有內存”,服務員再進行不同顧客上菜的時候就有了區分度,這樣就可以順利找到不同的顧客(切換不同的線程);在java線程中,程序計數器、虛擬機棧、本地方法棧都屬於線程私有的。

註意點:線程私有不存在多線程並發的資源競爭問題,因為其享有的內存是互不影響的,不存在並發問題

與線程私有相對,就有線程公有內存,這個區域在jvm虛擬機中有個特定的稱呼叫做:主內存

三:程序計數器:

對應於圖上標記的深紅色部分,主要是指程序在運行過程中所執行的字節碼(.class)文件的行號指示,也稱作行號指示器,程序流程的分支、循環、異常處理等基礎功能都需要它的引導來完成。它占有的內存空間比較小,它的運行的原理是:通過改變計數器的值來選取下一條的需要執行的字節碼命令,而這個值具體指的是虛擬機在Java方法中字節碼指令的地址,但是如果執行的是Native方法,那麽它的計數器的值是為空的。

註意

1:它是java中唯一一個沒有內存溢出(out of memory)情況的區域。可以思考一下這點是為什麽?它是進行線程運行的指示燈,如果沒有了它,程序也就無法運行了。

2:它屬於線程私有的:每個線程內部都有一個程序計數器,為了保證每個線程切換前後都能正常運行。

四:Java虛擬機棧:

虛擬機棧主要是作用在java方法運行時候,每個線程在運行到一個方法的入口都會創建一個棧幀,創建棧幀的目的在於存儲局部變量、操作數棧、動態鏈接、方法出口等信息。按照這個原理,那麽一個方法在從調用直到完全執行完畢,都會對應一個從入棧到出棧的過程。

這裏我們說明一下局部變量,它的含義就是定義在java方法內部中的變量,對應於java中8大基本數據類型,byte、int、float、boolean、char、short、long、reference:(long和double占用2個空間,其余1個)局部變量表裏我們需要註意的是,它在編譯期已經完全了內存分配,這樣棧幀在用局部變量的時候,它占用的內存是已經確認的,不需要再分配,這就一定程度上減少了棧幀的工作量。

註意:

1:當線程請求的棧幀的深度大於虛擬機允許的深度,就會拋出stackOverFlow異常

2:我們平時所說的堆棧,其中的棧就是指的是這裏的棧幀

3:它也是線程私有的,並且和線程的生命周期相同。

技術分享

圖 2-1:虛擬機棧

五:本地方法棧:

本地方法棧,顧名思義它代表的就是本地方法Native執行的棧幀,在jdk的源碼中我們可以看到很多命名為Native的方法,Native方法很大一部分是采用C/C++寫的,但是它對Native采用的數據結構、語言等都沒有做具體的要求,這一點完全是由java虛擬機進行實現的。

註意:

1:這個區域會拋出oom或者sof(stackoverflowError)異常

2:同樣它屬於本地線程私有的

六:Java堆:

Java堆主要是存儲對象的地方,同時它也是java內存管理區域最大的一塊。當我們在程序中new一個對象出來或者新建一個數組,就會把對象存儲這個區域。它是java的最重要的公共區域之一,與線程私有相對,它是屬於線程公有的。基本上所有的對象都會存儲在此區域,但也不是絕對的(隨著JIT技術的成熟,這一點會發生微變)。堆是主要存放對象的地方,同時它就會產生另一個問題就是GC,當垃圾進行回收的時候,虛擬機並沒有開辟新的空間,還在此區域進行,同時這個區域也叫“GC堆”,再細致劃分下去,分為新生代、老年代,同時還分為Eden空間、From survivor空間、To Survior區域。為什麽要劃分的這麽細致呢?其實還是主要還是為了更加有效率的回收,劃分的區域越細致,那麽垃圾回收器收集的時候只要去對應的地方直接回收就行,不用加上額外的判斷邏輯。

註意點:

1:堆在邏輯上是連續不間斷的內存空間,但是在物理上可以是不連續的內存空間

2:在堆中如果沒有完成內存的實例分配就會拋出oom

3:java堆是線程公有的,所有的線程共享這一片區域

技術分享

圖 3-1 java堆內存

七:方法區

方法區主要是用來存放已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據,比如我們在代碼中定義的Constant常量就會在這個區域存儲。在java虛擬機規範中,它是屬於堆的邏輯部分。同時在這個區域中,它也會有垃圾回收器工作,這個區域叫做“永久代”,之所以叫做永久代,因為它比新生代和老年代擁有更長的生命周期,但是並不是在這個區域它就會萬事大吉了,永久代依然會存在垃圾回收的情況,只不過相對來說較少

註意點

1:此區域屬於線程公有的,線程的類信息、常量、靜態變量、編譯代碼都在此區域進行存儲

2:此區域會拋出oom異常,發生在方法區無法進行內存分配時

技術分享

圖:4-1 方法區

八:運行時常量池:

運行時常量池主要是方法區的一部分,class文件除了有類的版本、字段、方法接口等描述信息外,還有一項信息是常量池,用於存儲編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區運行時常量池中存放。

註意:此區域同樣會發生oom異常

九:總結

本篇博文主要是對java的內存數據模型區域劃分進行了簡單的介紹,沒有深入細致過多的介紹。不過我們在整體上有一個感性的認識,理解java內存區域的劃分,以及這樣的劃分的好處,還有設計到線程的部分,理解並發的問題。這部分屬於虛擬機底層的東西,屬於進階。相信看完這篇文章後,我希望能回答以下幾個問題。同時這些東西也是考究我們對於底層的認識,面試中也是經常會被提及到的東西

1:java內存模型分為哪幾個區域?

2:請說出屬於線程私有部分和線程公有部分的區域?

3:我們都知道內存是在按照“代”進行劃分的,那麽請問java堆中內存分為哪幾代?分別位於什麽區域?

4:為什麽要把java內存劃分這麽多,劃分這麽多不嫌麻煩嗎?有什麽好處

我希望能思考以上問題,學習的時候就要進行多思考,深入思考這是為什麽?這篇博文的介紹就到此結束了,下一篇見,對於java內存模型的理解有助於我們處理並發的問題,這是屬於javaEE中高端進階必不可少的基礎,在此留個記錄,同時分享出來,讓大家也能清楚的認識Java內存模型同時提醒自己深入理解。

主要參考資料:

《深入Jvm虛擬機》

Java內存數據模型