虛擬機字節碼執行引擎
運行時棧幀結構
• 局部變量表
• 操作數棧
• 動態鏈接
• 方法返回地址
• 附加信息
局部變量表
• slot 32 64
• 數據類型:byte boolean short char int float double long reference(ccs) returnAddress
• slot 複用:當一個變量的pc寄存器的值大於Slot的作用域的時候,Slot可以複用
/**
* 局部變量表 複用slot
* -verbose:gc
*/
public class GCDemo {
public static void main(String[] args) {
{
byte[] buff = new byte[1024 * 1024 * 30];
}
int a = 0;
System.gc();
}
}
操作數棧
操作數棧(Operand Stack)也常被稱爲操作棧,它是一個後入先出(Last In First Out,LIFO)棧
動態連接
Class文件的常量池中存有大量的符號引用,字節碼中的方法調用指令就以常量池裏指向方法的符號引用作爲參數。這些符號引用一部分會在類加載階段或者第一次使用的時候就被轉化爲直接引用,這種轉化被稱爲靜態解析。另外一部分將在每一次運行期間都轉化爲直接引用,這部分就稱爲動態連接。
package jvm;
/**
* 動態鏈接
*/
public class Demo3 {
static class Super{
public void test() {
System.out.println("parent");
}
}
static class Sub1 extends Super {
public void test() {
System.out.println("Sub1");
}
}
static class Sub2 extends Super {
public void test() {
System.out.println("Sub2");
}
}
public static void main(String[] args) {
Super c1 = new Sub1();
Super c2 = new Sub2();
c1.test();
c2.test();
}
}
運行結果:
Sub1
Sub2
方法返回地址
方法調用時通過一個指向方法的指針,方法返回時將回歸到調用處,那個地址就是返回地址
方法調用
解析
調用目標在程序代碼寫好、編譯器進行編譯那一刻就已經確定下來。這類方法的調用被稱爲解析(Resolution)
• 靜態方法、構造器、私有方法,final修飾的方法
分派調用
靜態分派
package jvm;
/**
* 靜態分派示例
*/
public class Demo02 {
static class Super{
}
static class Sub1 extends Super {
}
static class Sub2 extends Super {
}
public void test(Sub1 sub1) {
System.out.println("sub1 is called");
}
public void test(Sub2 sub2) {
System.out.println("sub2 is called");
}
public void test(Super parent) {
System.out.println("parent is called");
}
public static void main(String[] args) {
Super c1 = new Sub1();
Super c2 = new Sub2();
Demo02 demo02 = new Demo02();
demo02.test((Sub1) c1);
demo02.test(c2);
}
// Code:
// stack=2, locals=4, args_size=1
// 0: new #7 // class jvm/Demo02$Sub1
// 3: dup
// 4: invokespecial #8 // Method jvm/Demo02$Sub1."<init>":()V
// 7: astore_1
// 8: new #9 // class jvm/Demo02$Sub2
// 11: dup
// 12: invokespecial #10 // Method jvm/Demo02$Sub2."<init>":()V
// 15: astore_2
// 16: new #11 // class jvm/Demo02
// 19: dup
// 20: invokespecial #12 // Method "<init>":()V
// 23: astore_3
// 24: aload_3
// 25: aload_1
// 26: checkcast #7 // class jvm/Demo02$Sub1
// 29: invokevirtual #13 // Method test:(Ljvm/Demo02$Sub1;)V
// 32: aload_3
// 33: aload_2
// 34: invokevirtual #14 // Method test:(Ljvm/Demo02$Super;)V
// 37: return
// LineNumberTable:
// line 27: 0
// line 28: 8
// line 29: 16
// line 30: 24
// line 31: 32
// line 32: 37
// LocalVariableTable:
// Start Length Slot Name Signature
// 0 38 0 args [Ljava/lang/String;
// 8 30 1 c1 Ljvm/Demo02$Super;
// 16 22 2 c2 Ljvm/Demo02$Super;
// 24 14 3 demo02 Ljvm/Demo02;
}
運行結果:
sub1 is called
parent is called
invokevirtual 靜態分派好了
動態分派
重要體現:重寫
package jvm;
/**
* 動態鏈接
*/
public class Demo3 {
static class Super{
public void test() {
System.out.println("parent");
}
}
static class Sub1 extends Super {
public void test() {
System.out.println("Sub1");
}
}
static class Sub2 extends Super {
public void test() {
System.out.println("Sub2");
}
}
public static void main(String[] args) {
Super c1 = new Sub1();
Super c2 = new Sub2();
c1.test();
c2.test();
}
}
運行結果:
Sub1
Sub2
在運行期根據實際類型確定方法執行版本的分派過程稱爲動態分派。
動態語言支持
何謂動態類型語言 ?動態類型語言的關鍵特徵是它的類型檢查的主體過程是在運行期而不是編譯期進行的,滿足這個特徵的語言有很多,常用的包括:APL、Clojure、Erlang、Groovy、JavaScript、Lisp、Lua、PHP、Prolog、Python、Ruby、Smalltalk、Tcl,等等。那相對地,在編譯期就進行類型檢查過程的語言,譬如C++和Java等就是最常用的靜態類型語言。
package jvm;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
* 動態語言
*/
public class DynamicLanguage {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine javaScript = scriptEngineManager.getEngineByName("JavaScript");
Object eval = javaScript.eval("function add(a,b) {return a+b;} add(2,3);");
System.out.println(eval);
}
}
點關注不迷路: