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

简介: 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引擎了。



相关文章
|
6月前
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
5月前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
122 0
|
2月前
|
存储 Java C++
JVM 运行时数据区
Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域。这 些区域都有各自的用途,以及创建和销毁的时间,有些区域随着虚拟机进程的启动而存在,有些区 域则是依赖线程的启动和结束而建立和销毁。Java 虚拟机所管理的内存被划分为如下几个区域 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解 析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳 转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成; 为什么要线程计数器?因为线程是不具备记忆功能 Java 虚拟机
|
5月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
5月前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
5月前
|
Java
JVM运行时数据区
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一
64 2
|
6月前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
120 1
|
6月前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
73 3
|
6月前
|
监控 Java 开发者
Java虚拟机(JVM)深度优化指南####
本文深入探讨了Java虚拟机(JVM)的工作原理及其性能优化策略,旨在帮助开发者通过理解JVM的内部机制来提升Java应用的运行效率。不同于传统的技术教程,本文采用案例分析与实战技巧相结合的方式,为读者揭示JVM调优的艺术。 ####
166 8
|
7月前
|
存储 算法 Java
聊聊jvm的内存结构, 以及各种结构的作用
【10月更文挑战第27天】JVM(Java虚拟机)的内存结构主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和运行时常量池。各部分协同工作,为Java程序提供高效稳定的内存管理和运行环境,确保程序的正常执行、数据存储和资源利用。
196 10