JVM学习-底层字节码的执行过程

目录

1.一个简单的程序分析

[2. a++,++a,a--在JVM中的执行过程](#2. a++,++a,a--在JVM中的执行过程)

[3. 一个好玩的x=x++](#3. 一个好玩的x=x++)

4.方法调用的字节码分析、多态的实现、对象头

[5. try-catch-finally的字节码分析](#5. try-catch-finally的字节码分析)

[5.1 try-catch](#5.1 try-catch)

[5.2 try-catch-finally](#5.2 try-catch-finally)

5.3特殊情况

[5.3.1 try和finally块中都出现了return](#5.3.1 try和finally块中都出现了return)

5.3.2出现多个catch块

6.加锁和解锁的字节码分析

7.静态成员变量初始化以及普通成员变量初始化的字节码分析

7.1静态成员变量初始化

7.2普通成员变量初始化

8.可变参数的实现


1.一个简单的程序分析

2. a++,++a,a--在JVM中的执行过程

3. 一个好玩的x=x++

在IDEA中也会提示x++的值未被使用:

4.方法调用的字节码分析、多态的实现、对象头

构造方法、私有方法、final方法使用的关键字是invokespecial,普通的public方法使用的是invokevirtual,静态方法使用的是invokestatic。当使用new方法创建对象时,会先在堆内存中分配空间,分配成功后会将对象的引用放到操作数栈,再通过关键字dup来对栈顶元素也就是这个引用进行复制,再通过invokespecial执行构造方法,构造完成后这个复制的对象引用就会被弹出,所以栈中还剩下原来的对象引用,最后会将这个引用赋给变量;倘若没有dup复制,那么在构造完成后就会将仅有的对象引用弹出,就无法赋给变量。静态方法不需要对象来调用,直接调用即可,如果使用对象调用静态方法,那么在底层的字节码指令中会先让对象的引用入栈,准备进行方法调用时发现是一个静态方法,那么就会将这个对象的引用弹出,然后直接通过invokestatic调用静态方法,这就会多执行一次入栈和弹出操作。

延伸,多态的实现:

多态是对一个接口中的所有方法进行多种方式的实现,比如一个接口Animal,有一个say方法,而对于不同的动物Dog和Cat,要实现Animal的say方法肯定是不一样的。

java 复制代码
//Animal接口
public interface Animal{

    //要实现的方法say
    public void say();

}

//Dog实现类
public class Dog{

    public void say(){
        System.out.println("汪汪");
    }

}

//Cat实现类
public class Cat{

    public void say(){
        System.out.println("喵喵");
    }

}

    //在main方法中选择一个实现类来实现say方法
    Animal am=new Dog();
    am.say();//输出"汪汪"

那么这种多态是怎么实现的呢?

首先要实现的方法必须是public的,而public的方法调用使用的JVM指令是invokevirtual,所以关键就在于invokevirtual是怎样调用方法的。当使用invokevirtual进行方法调用时,会先通过栈帧中的对象引用找到对象(也就是先找到new的Dog对象),然后从对象头中的类型指针找到它是哪个类的实例(即从new的这个Dog对象的对象头中的类型指针了解到这是一个Dog类的对象,类型指针指向的其实是这个类的Class对象,也叫做类镜像),这个类的Class对象中包含一个虚方法表vtable,这个vtable在类加载的链接阶段就已经根据方法的重写规则生成好了,从这个表中就能获得这个类中每个方法的入口地址(也就是Dog类的Class文件中有一个虚方法表,这个表里包含了其所有方法的入口地址,所以从这个虚方法表中就知道了Dog类的say方法在什么地方),根据这个地址就可以知道方法的字节码并去执行(查到了Dog类的say方法的地址后invokevirtual指令就会到这个地址找到对应的say方法并调用)。虚方法表实际上不在Class对象中,这里为了方便理解简化了,具体结合另一篇博客JVM学习-类加载的加载部分理解。

所以,如果将实现类换成Cat,那么所创建的对象的对象头中的类型指针指向的就是Cat的Class对象,然后从Cat类的虚方法表中找到Cat的say方法地址进而去调用。

上面涉及到了对象头,对象头的一部分是Mark Word,包含了对象运行时的数据,比如用于快速定位对象在数组或散列表中的位置的Hash Code,对象当前的寿命以及锁状态等;另一部分是对象的类型指针,指向类的Class对象,通过这个指针可以确定该对象是哪个类的实例。

5. try-catch-finally的字节码分析

5.1 try-catch

5.2 try-catch-finally

5.3特殊情况

5.3.1 try和finally块中都出现了return
5.3.2出现多个catch块

6.加锁和解锁的字节码分析

7.静态成员变量初始化<clinit>以及普通成员变量初始化<init>的字节码分析

7.1静态成员变量初始化<clinit>

Field表示成员变量,图中标错了。

7.2普通成员变量初始化<init>

源代码:

局部变量表:

字节码分析 :

注意区分局部变量表中的a、b和运行时常量池中的a、b,前面的a和b是有参构造方法中的两个参数,后面的a、b则是两个成员变量。普通成员变量和静态成员变量都是在常量池中的,所有的成员变量都是类的一部分,而常量池中存放着类的相关信息。

8.可变参数的实现

相关推荐
blammmp18 分钟前
Java:数据结构-枚举
java·开发语言·数据结构
暗黑起源喵36 分钟前
设计模式-工厂设计模式
java·开发语言·设计模式
Chef_Chen40 分钟前
从0开始学习机器学习--Day13--神经网络如何处理复杂非线性函数
神经网络·学习·机器学习
WaaTong41 分钟前
Java反射
java·开发语言·反射
九圣残炎1 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode
wclass-zhengge1 小时前
Netty篇(入门编程)
java·linux·服务器
lulu_gh_yu1 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
Re.不晚2 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
雷神乐乐2 小时前
Maven学习——创建Maven的Java和Web工程,并运行在Tomcat上
java·maven
码农派大星。2 小时前
Spring Boot 配置文件
java·spring boot·后端