JVM基礎結構與字節碼執行引擎

JVM基礎結構與字節碼執行引擎

JVM基礎結構
JVM內部結構如下:棧、堆。


JVM中的棧主要是指線程裏面的棧,裏面有方法棧、native方法棧、PC寄存器等等;每個方法棧是由棧幀組成的;每個棧幀是由局部變量表、操作數棧等組成。

每個棧幀其實就代表一個方法


java中所有對象都在堆中分配;堆中對象又分爲年輕代、老年代等等,不同代的對象使用不同垃圾回收算法。

-XMs:啓動虛擬機預留的內存
-Xmx:最大的堆內存

一、堆的分代假設
根據研究表明,堆中對象大部分都是創建後,立馬就可以被銷燬的。如:

爲了優化堆中的內存,將堆中對象分爲不同代。在年輕代中,GC發生比較頻繁;在老年代中,GC發生比較少。

二、堆的分代
年輕代:Young Generation
老年代:Old Generation/Tenured
永久代:Permanent Generation
永久代在Java虛擬機規範中是沒有的,但是Host Spot虛擬機中有。

三、方法區
方法區被所有線程共享;方法區是用來存儲編譯後的代碼,即存儲每個類的運行時常量池、字段和方法。
方法區在虛擬機啓動時創建;雖然方法區在邏輯上是堆的一部分,但在一些簡單的實現中,方法區可以選擇不進行垃圾回收和緊湊化。

方法區在java8的變化
java7之前:方法區的實現:永久代,是作爲堆的一部分;
java8之後:方法區的實現:metaspace,是堆外的內存;
1、爲什麼要這樣改變?
因爲java可以動態加載字節碼信息,這樣方法區就會慢慢的擠佔堆中內存。爲了避免與堆爭搶內存,java8將方法區的實現移至堆外。
2、方法區、永久代、MetaSpace的區別?
方法區是java虛擬機規範所規定的一個概念。其中java7實現方法區的地方稱爲永久代;java8實現方法區的地方稱爲MetaSpace

字節碼文件的結構
java程序在運行的時候,將源碼編譯成字節碼,字節碼在不同系統上的JVM翻譯成對應的機器碼。這是Java平臺無關性的基礎。

但是,編譯後的字節碼是如何讀取到JVM中的?字節碼執行引擎是如何識別、執行指令?

1、如何查看字節碼文件

classpy工具
IDEA的jclasslib Bytecode viewer插件
2、字節碼文件結構
一個字節碼文件包含以下部分:

(1)magic:0xCAFEBABE
class文件的magic code,用於標識該文件是class文件。

(2)minor_version、major_version
用於標識該class文件的版本,防止高版本的class文件被低版本的JVM讀取並執行。

(3)constant_pool:常量池
用於存儲該class文件經常被使用的信息,優化內存。比如說System.out.print()

(4)access_flag
表示這個類得訪問權限,對應到java源碼就是public、final之類的

字節碼執行引擎
這裏以一個線程爲例。一般來說,一個方法棧最底層的棧幀都是Thread.run方法。當一個線程準備調用另一個方法時,會先將實參拷貝一份到新棧幀的局部變量表裏,然後再執行代碼。
1、局部變量表
每次調用新方法時,會默認將當前對象的地址this作爲局部變量表的第一個參數;後面存放傳過來的參數。這與javascript的做法很相似。

2、方法調用的相關指令

invokevirtual:一般實例方法,有多態;
invokeinterface:接口方法,有多態
invokestatuc:靜態方法,無多態
invokespecial:特殊方法,無多態
invokedynamic:動態調用,JDK7新增,方法無需在編譯時確定
3、方法調用的過程
(1)在開始時

方法棧新增一個棧幀;
實例方法的this、參數放到局部變量表中;
開始新棧幀中字節碼的執行;
(2)在返回時

將返回值放在調用者方法棧幀中的操作數棧上;
(3)在異常出現時

尋找匹配的異常處理代碼
(4)在finally時

爲每個分支新增一個跳轉
4、爲什麼Mockito、EasyMock無法對private、static方法進行mock?
因爲他們mock方法是通過覆蓋這些方法來實現的,而private、static沒法被覆蓋。PowerMock是通過修改字節碼文件達到mock私有、靜態方法的。

原博客地址

轉載地址https://www.cnblogs.com/fourther/p/12673213.html

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