1. 程式人生 > >深入JAVA虛擬機之運行時數據區

深入JAVA虛擬機之運行時數據區

線程 例如 new object 垃圾收集 class文件 同學 結構 版本

前言
最近在啃一本書《深入JAVA虛擬機》,這本書不是第一次看,可以說是從大學就開始看,
這一次應該算第三次啃這本書,也應該說算是第一次真正啃這本書。大學的時候,只是好奇表層的一些神奇現象,隨著工作幾年後,現在回過頭來再次啃這本書,對於表層的那些以前覺得神奇的現在已經感覺乏味,反而對於底層是如何實現、如何運作的越來越著迷。這也是這次看這本書的初衷。通過寫博客記錄下自己的學習過程,也方便以後回頭看看現在的看法想法在將來會變成怎樣。如果我在下面的文字表述上或者理解上有誤解或者錯誤,請各位大神能夠留言指正,如果有跟我一樣愛好的同學,也歡迎留言探討,相互學習。


概述

       對於c/c++開發者而言,他們對內存管理擁有最高權利,但是需要幹最多“活”的勞動
人民。他們擔負者每一個對象生命周期從開始到結束的責任(需要分配內存,回收內存)。
對於java開發者而言,這些苦逼的活不需要做,因為都交給JVM的**自動內存管理機制**
去做了。有句調侃的話叫:人生苦短,我用XXXX.那麽java開發者真就輕松了嗎?
不!雖然把內存管理交給JVM去做了,但要是程序一旦出先內存泄漏、內存溢出等問題,
如果不清楚虛擬機是如何使用內存的,那麽排除錯誤將會比他們更加苦逼。

運行時數據區

java虛擬機在執行程序過程中,會把**管理的內存**,劃分為多個**數據區**。它們都有特
定的用途。包括:方法區,堆,程序計數器,虛擬機棧,本地方法棧,運行時常量池,直
接內存

1.程序計數器

它是一塊很小的內存。作用可看作是:當前線程所執行的字節碼的行號指示器。字節碼解
釋器就是通過它獲取一條需要執行的字節碼指令。java虛擬機多線程是通過線程輪流切換
並分配處理器時間來實現的,在確定的時間一個處理器,只會執行一條指令。所以為了線
程在切換時能夠回到在正確的位置,每一個線程都有獨立的程序計數器,是當前線程私有
的。各個線程之間的計數器是互不影響,獨立存儲的,稱之為線程私有的內存區域。
    特點:
        線程間互不影響,獨立存儲。

2.虛擬機棧

它也是線程私有的,生命周期與線程相同,是java方法執行時的內存模型。每個方法執
行時,都會創建一個棧幀,用於存儲局部變量表、操作棧、動態鏈接、方法出口等信息。
局部變量表存放了編譯期可知的基本數據類型,對象引用和return Address類型。

3.本地方法棧

為虛擬機使用native方法服務。它的作用和虛擬機棧非常相似,不同點就是服務的對象
不同,前者是給java方法服務,而後者是給native方法服務。

4.java堆

是java虛擬機中管理最大的一塊內存區域。是被所有線程所共享的內存區域。在虛擬機
啟動時創建,唯一目的就是存儲對象實例。是java垃圾收集器的主要管理區域,這裏又
被成為GC堆。從內存回收來看,現在的垃圾收集器實現大都是使用分代收集算法,
所以java堆還可以細分為:新生代,老年代,再細分為:eden空間,from servivor空間,
To Servivor空間。該片內存,可以在物理上不是連續的,只要邏輯上連續即可。
可動態擴展的(-Xms,-Xmx)。

5.方法區

所有線程共享,用於存儲虛擬機加載的類信息、常量、靜態變量、即使編譯的代碼等數據。

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

6.直接內存

並不是運行時數據區內存中的一部分,也不是虛擬機規範定義的內存,因為頻繁使用,也
可能出現OutOfMemoryError異常。在jdk1.4加入的NIO類,引入了一種基於通道/緩沖區
的I/O方式,它可以使用native函數庫直接分配堆外內存,通過存儲在java堆中
的DirectByteBuffer對象作為這塊內存的引用進行操作。

圖示:
技術分享圖片

總結:
    1.程序計數器,是線程私有的,線程間相互獨立,是唯一一個不會拋出OutOfMemoryError異常的運行時數據區。
    2.虛擬機棧,是線程私有的,與線程生命周期相同,是方法執行時的內存模型,通常說的棧內存指的就是這個
    虛擬機棧。
    3.本地方法棧,與虛擬機棧非常相似,服務對象不同,一個是服務Native方法,一個是服務Java方法。
    4.Java堆,最大的一塊內存,線程共享區,虛擬機啟動時創建,垃圾收集器的主要用武之地,所以這裏又叫
    GC堆。可動態擴展。
    5.方法區,所有線程共享,記錄虛擬機加載的類信息,常量、靜態變量、即時編譯的代碼。

現在也已經清楚了運行時數據區的職責劃分,那麽現在可以開始討論java是如何訪問對象的。

  • 對象訪問

java中對象訪問隨處可見,那麽你知道它是怎麽實現訪問對象的嗎?在java中就算最簡單的對象訪問都必然會
涉及到java棧、java堆、方法區這三個最重要的內存區域。舉例如下:

Object obj = new Object();

假設上面語句出現在一個方法體內,Object obj會被反映到java棧,作為一個reference類型數據出現。
而new Object()會反映到java堆中,在java堆中,開辟出一塊存儲object類型實例數據值的結構化內存,java堆中
還必須要保存對象類型數據的地址信息,而對象類型數據就保存在方法區內。

但是因為reference類型在java虛擬機規範中說指向一個對象的引用,並沒有說明該引用通過什麽方式去定位和如何訪問java堆中的對象具體位置。根據不同的虛擬機實現,會有不同的方式,主流的方式就兩種:句柄、直接指針
下面是兩種方式的示意圖:

技術分享圖片
技術分享圖片

深入JAVA虛擬機之運行時數據區