jvm(16) -- 虚拟机字节码执行引擎(运行时栈帧结构)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: jvm(16) -- 虚拟机字节码执行引擎(运行时栈帧结构)

前面研究了字节码结构,类加载的过程,现在了解,字节码执行。


一、运行时栈帧结构


1dc618a0ed9580ce8bfa6facb208c08f.png5d4c6812c8535adbb050f4ddf2e1bce8.png46a9d80a6e05e4e3b19d57a0ee70bcdf.png


1.局部变量表


①局部变量表必须赋初始值


66ba272a0bfc97be54a5fa679e3d5482.png


②如何存储和占用内存大小


使用slot存储,根据数据类型有使用1个slot的类型,也有2个slot的类型的。


1dc618a0ed9580ce8bfa6facb208c08f.png


引起线程安全的三个条件:


① 多线程


②共享资源


③共享资源进行非原子性操作


③slot可以复用



5d4c6812c8535adbb050f4ddf2e1bce8.png

46a9d80a6e05e4e3b19d57a0ee70bcdf.png


代码演示:


public class GCDemo {
    public static void main(String[] args) {
        {
            byte[] buff = new byte[60 * 1024 * 1024];
        }
        //int a = 10;
       System.gc();
    }
}


1dc618a0ed9580ce8bfa6facb208c08f.png


打开注释代码: int a = 10;


5d4c6812c8535adbb050f4ddf2e1bce8.png


原因:

46a9d80a6e05e4e3b19d57a0ee70bcdf.png


2.操作数栈


①定义和工作过程


Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指-操作数栈。


操作数栈也常被称为操作栈。


和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。


虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的:如int、long、float、double、reference和returnType的存储。对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。


虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中,看看下面的示例,它演示了虚拟机是如何把两个int类型的局部变量相加,再把结果保存到第三个局部变量的:


begin
iload_0 // push the int in local variable 0 onto the stack
iload_1 // push the int in local variable 1 onto the stack
iadd // pop two ints, add them, push result
istore_2 // pop int, store into local variable 2
end

在这个字节码序列里,前两个指令iload_0和iload_1将存储在局部变量中索引为0和1的整数压入操作数栈中,其后iadd指令从操作数栈中弹出那两个整数相加,再将结果压入操作数栈。第四条指令istore_2则从操作数栈中弹出结果,并把它存储到局部变量区索引为2的位置。下图详细表述了这个过程中局部变量和操作数栈的状态变化,图中没有使用的局部变量区和操作数栈区域以空白表示。

1dc618a0ed9580ce8bfa6facb208c08f.png


②操作数栈共享区域


5d4c6812c8535adbb050f4ddf2e1bce8.png


3.动态链接


在Class文件中的常量持中存有大量的符号引用。字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数。这些符号引用一部分在类的加载阶段(解析)或第一次使用的时候就转化为了直接引用(指向数据所存地址的指针或句柄等),这种转化称为静态链接。而相反的,另一部分在运行期间转化为直接引用,就称为动态链接。


与那些在编译时进行链接的语言不同,Java类型的加载和链接过程都是在运行的时候进行的,这样虽然在类加载的时候稍微增加一些性能开销,但是却能为Java应用程序提供高度的灵活性,Java中天生可以动态扩展的语言特性就是依赖动态加载和动态链接这个特点实现的。


动态扩展就是在运行期可以动态修改字节码,也就是反射机制与cglib。


4.方法返回地址


46a9d80a6e05e4e3b19d57a0ee70bcdf.png


两种退出方式


66ba272a0bfc97be54a5fa679e3d5482.png


5.附加信息


1dc618a0ed9580ce8bfa6facb208c08f.png


二、方法调用


方法调用



5d4c6812c8535adbb050f4ddf2e1bce8.png


1.解析调用


46a9d80a6e05e4e3b19d57a0ee70bcdf.png66ba272a0bfc97be54a5fa679e3d5482.png



以下四种情况不能重载或者从写:

1dc618a0ed9580ce8bfa6facb208c08f.png


也就对应下面的指令:

5d4c6812c8535adbb050f4ddf2e1bce8.png46a9d80a6e05e4e3b19d57a0ee70bcdf.png66ba272a0bfc97be54a5fa679e3d5482.png


看下解析调用已经确定好了。演示如下:

public class Hello {
    public  static void sayHello(){
        System.out.println("hello world");
    }
    public static void main(String[] args) {
        Hello.sayHello();
    }
}

编译后,执行javap -verbose Hello.class.


可以看到main方法中的invokestatic指令指定了sayHello,方法已经确定,不可修改,这就是方法调用解析:

1dc618a0ed9580ce8bfa6facb208c08f.png

5d4c6812c8535adbb050f4ddf2e1bce8.png



2.分派调用


46a9d80a6e05e4e3b19d57a0ee70bcdf.png


①静态分派调用


静态分派也是在编译阶段就可确定。


例子:

public class Demo {
    static abstract class Human{
    }
    static class Man extends Human{
    }
    static class Woman extends Human{
    }
    public void sayHello(Human guy){
        System.out.println("hello,guy");
    }
    public void sayHello(Man guy){
        System.out.println("hello,gentleman");
    }
    public void sayHello(Woman guy){
        System.out.println("hello,lady");
    }
    public static void main(String[] args) {
        Human man = new Man();
        Human woman = new Woman();
        Demo demo = new Demo();
        demo.sayHello(man);
        demo.sayHello(woman);
    }
}

运行结果:


1dc618a0ed9580ce8bfa6facb208c08f.png


why?


5d4c6812c8535adbb050f4ddf2e1bce8.png46a9d80a6e05e4e3b19d57a0ee70bcdf.png


执行javap -verbose Dmeo.class再来验证下:看下

66ba272a0bfc97be54a5fa679e3d5482.png


②动态分派调用


1dc618a0ed9580ce8bfa6facb208c08f.png


例子:


public class Demo {
    static abstract class Human {
        protected abstract void sayHello();
    }
    static class Man extends Human {
        @Override
        protected void sayHello() {
            System.out.println("man say hello");
        }
    }
    static class WoMan extends Human {
        @Override
        protected void sayHello() {
            System.out.println("woman say hello");
        }
    }
    public static void main(String[] args) {
        Human man = new Man();
        Human woMan = new WoMan();
        man.sayHello();
        woMan.sayHello();
        man = new WoMan();
        man.sayHello();
    }
}


运行结果:

5d4c6812c8535adbb050f4ddf2e1bce8.png


why?


46a9d80a6e05e4e3b19d57a0ee70bcdf.png66ba272a0bfc97be54a5fa679e3d5482.png88b9988b40447cb37c7e3c492d49867f.png


三、动态语言支持


1dc618a0ed9580ce8bfa6facb208c08f.png

java8以后引入新的js引擎,而JS就是动态类型的语言。说明java也支持了动态语言,当然java8之前就有js引擎了。



相关文章
|
26天前
|
存储 Java 编译器
JVM系列7-虚拟机字节码执行引擎
JVM系列7-虚拟机字节码执行引擎
15 1
|
13天前
|
存储 Java 机器人
Java中的字节码与JVM指令集详解
Java中的字节码与JVM指令集详解
|
13天前
|
存储 安全 Java
JVM之内存结构
Java内存结构包括程序计数器、虚拟机栈、本地方法栈、堆和直接内存。程序计数器记录执行地址,线程私有,无溢出。虚拟机栈处理方法调用,局部变量在线程栈中,过深或过大可能导致StackOverflowError。本地方法栈服务于native方法。堆存储对象,线程共享,有垃圾回收。方法区存储类信息,1.8前的永久代,1.8后的元空间,溢出可调整相应参数。运行时常量池包含字符串池,1.6在永久代,1.8在堆,intern方法管理。直接内存用于NIO,提高读写性能,手动回收。
|
18天前
|
存储 Java 编译器
【搞定Jvm面试】 面试官:谈谈 JVM 类文件结构的认识
【搞定Jvm面试】 面试官:谈谈 JVM 类文件结构的认识
|
26天前
|
存储 XML 安全
JVM系列5-类文件结构
JVM系列5-类文件结构
11 0
|
9月前
|
存储 安全 Java
【虚拟机字节码执行引擎】
【虚拟机字节码执行引擎】
|
存储 Java
虚拟机字节码执行引擎
虚拟机字节码执行引擎
70 0
|
Java 编译器 索引
虚拟机字节码执行引擎
一、概述 物理机的执行引擎:直接建立在处理器、硬件、指令集和操作系统层面 虚拟机的执行引擎:由自己实现,可以自行制定指令集与执行引擎的结构体系,并且能够执行不被硬件直接支持的指令集格式。 java虚拟机的执行引擎:输入字节码文件,处理过程是字节码解析的等效过程,输出是执行结果。
13779 0
|
存储 Java 索引
虚拟机字节码执行引擎
运行时栈帧结构 栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息。
1010 0
|
Java C++ 索引
深入理解虚拟机之虚拟机字节码执行引擎
执行引擎是java虚拟机最核心的组成部件之一。虚拟机的执行引擎由自己实现,所以可以自行定制指令集与执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令集格式。
8815 0