JVM之java虛擬機詳解

          jvm是java虛擬機的簡稱,jvm是一種用於計算機設備的規範。它是一個虛構出來的計算機,通過在真實的計算機上模擬各種計算機的功能來實現。java語言有一個非常重要的特點就是跨平臺性,而使用java虛擬幾是實現這一特點的關鍵。
     下圖是一張顯示jvm核心組件的示意圖。
   
 
   一,上圖顯示的組件分爲兩個內容來看,一個是線程創建的組件,一個是線程共享的組件。
    線程:JVM系統線程、每個線程相關的、程序計數器、棧、本地棧、棧限制、棧幀、局部變量數組、操作數棧、動態鏈接
    線程共享:堆、內存管理、非堆內存、即時編譯、方法區、類文件結構、類加載器、更快的類加載、方法區在哪裏、類加載器參考
                      運行時常量池、異常表、符號表、Interned字符串。
   二,Hotsopt解釋:SUN的JDK版本從1.3.1開始運用HotSpot虛擬機, 2006年底開源,主要使用C++實現,JNI接口部分用C實現。
     HotSpot是較新的Java虛擬機,用來代替JIT(Just in Time),可以大大提高Java運行的性能, Java原先是把源代碼編譯爲字節碼
在 虛擬機執行,這樣執行速度較慢。而HotSpot將常用的部分代碼編譯爲本地(原生native) 代 碼,這樣大大加速運行的速度。
   三、線程
      這裏所說的線程是指程序執行過程中的一個線程實體,JVM允許一個應用併發執行多個線程。Hotspot JVM中的java線程與原生操作系統的線程有直接的映射關係。當線程本地存儲、緩衝區分配、同步對象、棧、程序計數器等準備好以後,就會創建一個操作系統原生線程。java線程結束,原生線程隨之被回收。操作系統負責調度所有線程,並把他們分配到任何可用的cpu上。當原生線程初始化完畢,就會調用java線程的run()方法。此方法返回時會被處理未捕獲異常,原生線程將會確認由於它的結束是否要終止JVM進程。當線程結束時會釋放原生線程和java線程的所有資源。
   四、JVM系統線程
      如果使用調試器,你會看到很多線程在後臺運行。這些後臺線程與觸發public static void main(String[] )函數的主線程以及主線程創建的其他線程一起運行。 JVM後臺運行的系統線程主要有下面幾個:
    虛擬線程(VM thread):這個線程等待JVM到達安全點操作出現。這些操作有,垃圾回收、線程dump、線程暫停、線程偏向鎖接觸。
   週期性任務線程 :這個線程負責定時器事件,用來調度週期性操作的執行。
   GC線程: 這些線程支持JVM中不同的垃圾回收活動。
   編譯器線程: 這些線程在運行時將字節碼動態編譯成本地平臺的相關機器碼。
  信號分發線程: 這些線程接收發送到JVM的信號並調用適當的JVM方法處理。
  五、線程相關組件
   1、程序計數器(PC)
     PC指當前指令的地址,本地指令除外。所有的cpu都有一個pc,典型狀態下,每執行一條指令pc都會自增,因此pc存儲了指向下一條要被執行的指令的地址。JVM用pc來跟蹤指令執行的位置。pc將實際上時指向方法區的一個內存地址。
   2、棧(Stack)
    每個線程擁有自己的棧,棧包含每個方法執行的棧幀。棧時一個後進先出的數據結構,因此當前執行的方法在棧的頂部。每個方法調用時,一個新的棧幀創建並壓棧到棧頂。當方法正常返回或拋出未捕獲異常時,棧幀就會出棧。除了棧幀的壓棧和出棧,棧不能被直接操作。所以可以在堆上分配棧幀,並且不需要連續內存。
  3,Native棧
   並非所有的jvm實現都支持本地native方法,那些提供支持的jvm一般都會爲每個線程創建本地方法棧。如果JVM使用JNI,那麼本地棧就是一個C棧。在這種情況下,本地方法棧的參數順序,返回值,和典型的C程序一樣。本地方法一般可以反過來調用JVM中的java方法。這種native方法調用java會發生在棧上(java層的棧)。線程將離開本地方法棧,並在java棧上開闢一個新的棧幀。
  4,棧的限制
   棧可以動態分配也可以固定大小,如果線程請求一個超過允許範圍的控件,就會拋出StackOverflowError。如果線程西藥一個新的棧幀,但沒有足夠的內存可以分配,就會拋出一個OutOfMemoryError。
 5,棧幀
  每次方法調用都會創建一個新的棧幀並把它壓棧到棧頂。當方法正常返回或者調用過程中拋出未捕獲的異常時,棧幀將出棧。每個棧幀包含:局部變量數組、返回值、操作數棧、類當前方法的運行時常量池引用。
6,局部變量數組
  局部變量數組包含了方法執行過程中所有變量,包括this引用、所有的方法參數】其他局部變量。對於類方法也就是靜待方法,方法參數從下標0開始,對於對象方法,位置0保留爲this。除了long和double類型外,所有的變量類型都佔用局部變量數組中的一個位置。long和double需要佔用局部變量數組兩個連續的位置,因此他們時64位雙精度,其他類型都是32位單精度。
7,操作數棧
 操作數棧在執行字節碼指令過程中被用到,大部分JVM字節碼把時間花費在操作數棧的操作上:入棧、出棧、複製、交換、產生消費變量的操作。
8,動態鏈接
  每個棧幀都有一個運行時常量的引用。這個引用指向棧幀當前運行方法所在類的常量池。通過這個引用支持動態鏈接。
  java類文件編譯時,所有變量和方法的引用都會被當作富豪引用存儲在這個類的常量池中。符號引用是一個邏輯引用,實際上並不指向物理內存地址。JVM可以選擇引用解析的時機,一種時類文件加載並校驗通過後,這種解析方式被成爲飢餓模式。另外一種時符號引用在第一次使用時候被解析,這種解析方式成爲惰性模式。
六、線程間共享
  1,堆
   堆被用來在運行時分配類實例,數組。不能在棧上存儲數組和對象。因爲棧幀被設計位創建以後無法調整大小。棧幀只存儲指向堆中對象或數組的引用。與局部變量數組中的原始類型和引用類型不同。對象總是存儲在堆上以便在方法結束時不會被移除。對象只能由垃圾回收器移除。
 2,內存管理 (Eden 和 Survivor)
     對象和數組永遠不會被顯式回收,而是由垃圾回收器自動回收。通常式這樣:
     1,新的對象或數組被創建並放入老年代
     2,垃圾回收發生在新生代。以後存活的對象將從deen區移到survivor區。
     3,垃圾回收一般會導致應用進程暫停,他將在三個區內移動對象,仍存活的對象將被從新生代移動到老年代。
     4,每次進行老年代回收時也會進行永久代回收。他們之中任何一個變瞞時,都會進行回收。
 3,複製算法:將區域分成兩部分,其中一部分作爲保留空間,另一部分作爲使用空間、當發生垃圾回收時,首先檢查使用空間裏有哪些對象是存活的,檢查完之後把存活的對象複製到保留空間(這樣複製過來的好處是減少了內存碎片,如果直接在使用空間清除的話,那空間會很零散)裏,然後清洗使用 空間 。
 4,非堆內存
  非對內存指的是那些邏輯上屬於JVM一部分對象,但實際上不在堆上創建
  非堆內存對象包括:永久代:方法區、駐留字符串。 代碼緩存、用於編譯和存儲被那些編譯器編譯成原生代碼的方法。
5,即時編譯
  java字節碼時解釋執行的,但是沒有直接在jvm宿主執行原生代碼快。爲了提高性能,Oracle Hotspot虛擬機會找到執行最頻繁的字節碼片段並把他們編譯成原生的機器碼。編譯出的原生機器碼被存儲在非堆內存的代碼緩存中。通過這種方法,Hotspot虛擬機將權衡下面兩種時間消耗:將字節碼編譯成本地代碼需要的額外時間和解釋執行字節碼消耗更多的時間。
6,方法區
  方法區存儲了每個類的信息,比如:運行時常量、字段數據(字段名,字段屬性,字段類型,修飾符)、方法數據(方法名、返回值類型、參數類型、修飾符)、方法代碼(字節碼、操作數大小、局部變量大小、局部變量表、異常表) 所有線程共享同一個方法區,因此訪問方法區的和動態鏈接的進程必須時線程安全。如果兩個線程視圖訪問同一個還未加載的類的字段或方法,必須只加載一次,而且兩個線程必須等它加載完畢才能繼續執行。
7.

方法區在哪裏

The Java Virtual Machine Specification Java SE 7 Edition 中寫得很清楚:“儘管方法區邏輯上屬於堆的一部分,簡單的實現可以選擇不對它進行回收和壓縮。”。Oracle JVM 的 jconsle 顯示方法區和 code cache 區被當做爲非堆內存,而 OpenJDK 則顯示 CodeCache 被當做 VM 中對象堆(ObjectHeap)的一個獨立的域。

Classloader 引用

所有的類加載之後都包含一個加載自身的加載器的引用,反過來每個類加載器都包含它們加載的所有類的引用。

文章參考:http://www.importnew.com/17770.html

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