JVM从入门到精通(五): Java运行时数据区和常用指令
JVM Runtime Data Area and JVM Instructions
Java運行時數據區以及JVM指令
i=i++結果為8
i=++i結果為9
一個class的生命周期
以下面的規范為準:
運行時數據區的構成
PC:peogram counter 程序計數器
DirectMemory:直接內存,JVM可以直接訪問內核空間的內存(OS管理的內存),零拷貝(不需要拷貝),NIO用到了,提高效率
MethodArea:方法區,里面有常量池
PC 程序計數器
JVM ??臻g
在每一個線程創建的時候,線程會有自己獨立的JVM棧空間,棧中存放的是棧幀
堆空間
所有線程共享同一個堆空間。
堆空間是用來存放所有類實例和數組空間分配的運行時數據區。
方法區
所有線程共享同一個方法區。
方法區用來存放 per-class structors
Perm Space 與 Meta Space容易混淆,他們只是Method Area的兩的不同版本的實現。
運行時常量池
棧幀
- 局部變量
- 操作數棧
- 動態鏈接
指向運行時常量池里面的符號鏈接,看有沒有解析,如果沒有解析,就動態解析,如果已經解析了,就拿過來使用。例如,A方法要調用B方法,B方法在哪兒呢?就要去常量池里面找。 - 返回值地址
A方法調用了B方法,如果有返回值,要記錄返回值返回到那個地方,也就是記錄繼續執行的位置。
回到面試題
- 理解局部變量表
- 理解操作數棧
- 理解一些常用的指令
i=i++ 生成的指令
執行過程分析
i=++i 生成的指令
下面這個 i 因為超過了 127,所以用的是sipush,而不是上圖中的bipush
為什么我們可以在非static方法中使用this?因為this在局部變量表中是已經存在的。
(局部變量表中,0位置是this,1位置是k,2位置是i)
下面這個例子,之前有一道面試題:DCL 單例為什么要加 volitile?因為你看下面的第一條指令,我們知道,剛new出來對象是半初始化的對象,只是賦一個默認值,而involespecial才是調用構造方法,給變量賦初始值,而這兩條指令之間是可能會發生指令重排的。
返回值
遞歸的調用
下面是m方法的執行,沒有把main方法放上來
在這個3層遞歸中,使用到的是3個棧
另外,我們看到指令前面的數字 0,1,2,5,6,…,沒有3,4的原因,是2指令的字節數比較多,占用了后面的字節數
總結
invokeXXX指令
invokestatic
調用一個靜態方法
invokevirtual
new一個對象,調用一個非靜態方法
自帶多態:new 的是哪個對象,調用的就是哪個對象的方法
invokespecial
調用實例方法;對超類、私有和實例初始化方法調用的特殊處理
可以直接定位的,不需要多態的方法
- private方法
- 構造方法
final方法不是invokespecial的,它是invokevirtual的。
invokeinterface
調用接口方法
invokedynamic
JVM最難的指令
invokedynamic是lambda表達式或者反射或者其他動態語言scala kotlin,或者CGLib ASM,動態產生的class,會用到的指令
每一個lambda表達式都有一個自己的內部類,java沒有純粹的函數。匿名內部類每次都是動態產生的。
package com.mashibing.jvm.c4_RuntimeDataAreaAndInstructionSet;public class T05_InvokeDynamic {public static void main(String[] args) {I i = C::n;I i2 = C::n;I i3 = C::n;I i4 = () -> {C.n();};System.out.println(i.getClass());System.out.println(i2.getClass());System.out.println(i3.getClass());for(;;) {I j = C::n;} //MethodArea <1.8 Perm Space (FGC不回收)}@FunctionalInterfacepublic interface I {void m();}public static class C {static void n() {System.out.println("hello");}} }關于Lambda表達式的一個坑
如果你用Lambda表達式寫出了這樣的代碼:
在1.8之前有一個巨大的bug,就是你在里面產生了很多對象,但是Perm Space在FGC的時候是不會回收的。
Perm Space和Meta Space的區別?
字符串常量位于PermSpace
FGC不會清理
大小啟動的時候指定,不能變
字符串常量位于堆
會觸發FGC清理
不設定的話,最大就是物理內存
思考:
如何證明1.7字符串常量位于Perm,而1.8位于Heap?
提示:結合GC, 一直創建字符串常量,觀察堆,和Metaspace
總結
以上是生活随笔為你收集整理的JVM从入门到精通(五): Java运行时数据区和常用指令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL调优(四):MySQL索引优化
- 下一篇: HotSpot源码(二):Java与C语