JVM虛擬機結構圖
程序計數器
每個線程有單獨的程序計數器,程序計數器的指針永遠指向下一條要執行的指令
線程私有方法區
類的類型信息,常量池
線程共享Java堆內存
通過new關鍵字創建的對象實例,都保存在Java堆裏面,這一塊內存區域是GC回收的主要地方。而創建的對象的類型信息取方法區去取。
線程共享Java棧內存
Java棧總是跟線程關聯在一起,每當創建一個線程,JVM就回爲線程分配一個Java棧空間,而每個Java棧又包含多個棧幀組成,用於存儲局部變量表、操作棧、方法返回值等。
線程私有
對象的創建
JVM在方法區開闢空間存放對象的類信息,在Java堆內存中存放對象的實例信息,然後將引用指針給引用對象。
對象模型圖
垃圾回收
http://blog.csdn.net/jiafu1115/article/details/7024323
主要涉及三個問題:
如何判定對象爲垃圾對象
- 引用計數器
- 可達性分析算法
如何回收
- 標記清除
- 標記複製
- 標記壓縮
- 分代收集算法
- 何時回收
- Serial
- Parnew
- Cms
- G1
內存分配策略
- 優先分配到eden
- 大對象直接分配到老年代
- 長期存活的對象分配到老年代
- 空間分配擔保
- 動態對象年齡判斷
Class文件
- 魔術 0xCAFEBABE
- Class文件版本
- 常量池
- 訪問標誌
- 類索引 父類索引 接口索引的集合
- 字段表集合
- 方法表集合
- 屬性表集合
https://blog.csdn.net/wangtaomtk/article/details/52267621
字節碼指令
基本知識
- 操作碼和操作數組成
- 操作碼的長度是1個字節,最多256條
- 基於棧的指令集架構
分析命令
javap -verbose xxx.class
指令
iload iconstant
iadd
ireturn大多數指令都包含類型信息,
也有沒有 goto- 運算指令
- add 加
- sub 減
- mul 乘
div 除
…類型轉換指令
- i2l int 轉換成 long
- i2b int 轉換成 byte
- i2c int 轉換成 char
…
對象創建與訪問指令
- 創建類實例的指令 new
- 創建數組的指令 newarray anewarray
- 訪問類字段 getfield putfield getstatic putstatic
…
操作數棧管理指令
- 操作數棧指令用於直接操作操作數棧
- pop dup
- swap 棧頂的交換
…
控制轉移指令
- 條件分支 ifeq ifneq
- 無條件分支 goto
- 複合條件分支 tableswitch
…
方法調用指令
- invokevirtual 調用對象實例方法
- invokeinterface 調用接口
- invokespecial 調用特殊處理方法 實例化方法,初始化方法
異常處理指令
- athrow
同步指令
- 同步結構都是由管程(Monitor)來支持的
- synchronized 由monitorenter和monitorexit兩個指令來實現的。
類加載機制
加載 -> 連接 -> 初始化 -> 使用 -> 卸載
連接: 驗證 準備 解析
https://www.cnblogs.com/xlyslr/p/5751039.html
類加載的過程
加載
- 通過一個類的全限定名來獲取定義此類的二進制流
- 將這個字節流代表的靜態存儲結構轉換化成方法區的運行時的數據結構
- 將內存中生成的一個代表這個類的Class對象,作爲這個類的各種數據的訪問入口
驗證
- 文件格式驗證
- 元數據驗證
- 字節碼驗證
- 符號引用驗證
準備
準備階段正式爲類變量分配內存並設置變量的初始值。這些變量使用的內存都將在方法區中進行分配。
解析
- 虛擬機將常量池中的符號引用替換成直接引用的過程
符號引用
符號引用是一個字符串,它給出了被引用的內容的名字並且可能會包含一些其他關於這個被引用項的信息——這些信息必須足以唯一的識別一個類、字段、方法。這樣,對於其他類的符號引用必須給出類的全名。對於其他類的字段,必須給出類名、字段名以及字段描述符。對於其他類的方法的引用必須給出類名、方法名以及方法的描述符。
直接引用
- 直接指向目標的指針(比如,指向“類型”【Class對象】、類變量、類方法的直接引用可能是指向方法區的指針)
- 相對偏移量(比如,指向實例變量、實例方法的直接引用都是偏移量)
- 一個能間接定位到目標的句柄
解析分類:
- 類和接口的解析
- 字段解析
- 類方法解析
初始化
類加載的最後一步
執行類構造器()方法執行的過程
** 重點過程
類加載的時機
- 初始化
- 當遇到new/getstatic/putstatic/invokestatic 這4條字節碼指令時,如果類沒有進行初始化過,則需要先觸發其初始化;
- 使用Java.lang.reflect包的方法對類進行反射調用時候,如果類沒有進行初始化,則需要先觸發其初始化;
- 當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化;
- 當虛擬機啓動時,用戶需要指定一個要執行的主類(main()方法),虛擬機會先初始化這個主類
- 不被初始化的情況
- 通過子類引用父類的靜態字段,子類不會被初始化
- 通過數組定義來引用類
- 調用類的常量
類加載器
- 啓動類加載器
- 擴展類加載器
- 應用程序類加載器
自定義類加載器
雙親委派模型 jdk1.2之後出現
虛擬機字節碼執行引擎
JVM 內存基礎概念之程序計數器與 Java 虛擬機棧和本地方法棧
http://blog.csdn.net/airsaid/article/details/50619638
深入JVM字節碼執行引擎
http://blog.csdn.net/dd864140130/article/details/49515403
運行時棧幀結構
局部變量表
- slot變量槽 32位
- byte boolean short char int float double long reference
- long與double佔倆相鄰slot,其它類型佔一個slot。
- 當一個變量的PC寄存器的值大於作用域時,slot是可以複用的。
操作數棧
- 動態鏈接
方法返回地址
- 方法調用時通過一個指向方法的指針指向方法的地址,方法返回時將回歸到調用處,哪個地方是返回地址
附加信息
方法調用
http://blog.csdn.net/sunxianghuang/article/details/52280002
方法調用並不等於方法執行,方法調用階段的唯一任務就是確定被調用方法的版本解析
- invokestatic 靜態方法
- invokespecial 構造器方法和私有方法等等
- invokevirtual
- invokeinterface
- invokedynamic
- 編譯階段就能確定下來的方法版本 靜態方法 私有方法等等。
分派
靜態分派
- 方法的重載
- 靜態類型分派的
- 編譯階段已經確定
- 向下轉化
動態分派
- 方法的重寫
- 運行時期決定的
動態語言支持