Java虚拟机(十六)--类方法解析

Java方法的解析大体上分为3步:

  1. 在Java源代码编译期间,编译器负责将Java类源代码翻译为对应的字节码指令,同时完成的工作还有Java方法的局部变量表的计算,以及最大操作数栈的计算。
  2. 在jvm运行期间,jvm加载类型,调用classFileParser:parseClassFile()函数对Java class字节码文件进行解析,这一步将会完成Java方法的解析、字节码指令存放、父类与接口类方法继承与重载等一系列逻辑。
  3. 在调用系统类加载器SCL对应用程序的Java类进行加载过程中,完成方法符号链接、验证,最重要的是完成vtable与itable的构建,从而支持在jvm运行期的方法动态绑定。

对于步骤2,解析方法时,先从字节码文件中获取方法总数。
然后遍历每个方法,创建相应的mehodoop(也就是方法对象)。
创建方法对象的步骤主要有以下几步:

  1. 读取和验证Java方法的访问标识、名称以及描述符
  2. 解析方法的属性
  3. 创建methodoop
  4. 复制字节码

方法的访问标识,名称、描述符构成方法的签名。这3个各占2个字节。

属性常见的有Code,LVT(局部变量表),具体内容可以看十三篇的附录。


methodOop对象中保存方法的访问标识,虚方法表索引,方法大小,方法名等信息,内部constMethodOop保存了方法的字节码,局部变量表,异常表,字节码与源码行对应值、最大操作数栈等信息。

< init>方法是编译器自动生成的构造方法。
在没有明确定义构造方法时,init方法就是构造方法,所有类变量非静态的都在这里初始化。所有非静态代码块的内容也在这执行

其实在字节码层次上,所有的构造方法都是叫init,只是参数不同。不管是手工生成的,还是编译器自动生成的。
如果定义了多个构造方法,那么每个构造方法其实都会初始化类变量。
当然,一个构造方法明确调用另一个构造方法的话,只会在被调用的构造方法中做类变量的初始化工作。

< clint>方法也是编译器自动生成的,执行所有静态变量的初始化以及静态代码块。


vtable(虚函数表)是Java多态中的一个十分关键的技术。

在jvm加载类的过程中,会动态解析Java类的方法及其对父类方法的重写,进而构建出一个vtable分配到instanceKlass内存区的尾部,从而支持运行期动态绑定。

计算vtable主要分为2步:

  1. 获取父类vtable大小,并将当前类的vtable的设置为父类vtable的大小
  2. 循环遍历当前Java类的每一个方法,并调用need_new_vtable_entry()函数判断,如果是true,则vtable增加1

need_vtable_entry():

  1. 检查方法有没有被final,static修饰,或者是构造函数,或者类被final修饰。满足一条都返回false
    2.检查方法是否被private修饰,如果是则返回true
  2. 检查父类有没有这个方法,父类方法是不是public或者protect的。如果是,也就是说子类重写了父类的方法,返回false

如果Java类中声明了一个非private,非final,非static的方法:如果该方法是对父类方法的重写,那么就直接更新从父类继承来的vtable的方法指针;
如果不是对父类方法的重写,那么向这个类的vtable中新增一个位置,指向该方法的内存位置。

vtable分配在instanceKlass对象实例末尾,而instance在jdk8中固定占用0x1b8大小,所以得到类对应的InstanceKlass实例地址,就可以得到虚方法表。
比如有下面2个类, Test1和p

public class Test1 extends p{
    public int a=0;
    static Integer si=6;
    String s="Hello world";

    public Test1(int d){

    }

     final void do1(){
        a++;
        System.out.println(a);

    }
    public static void main(String[] args) {
        Test1 test1=new Test1(4);
        test1.do1();
        test1.a=8;
        si=9;
        int d=si;
    }

    private void test(){
        this.a=a;
    }
}
class p{
     void do1(){}
}

在这里插入图片描述

是hsdb查看内存,可以发现
Test1的虚方法表长度为7
在这里插入图片描述
父类p的虚方法表长度为6
在这里插入图片描述
查看内存中相应的地址,可以看到从object类中继承来的前5个方法都是一样的,后面的方法不一样。
在这里插入图片描述

查看父类p中第6个方法,对应的名字,是常量池第11个元素,也就是do1
在这里插入图片描述
子类Test1中虚方法表中最后一个方法就是那个私有方法test()。


miranda方法
指那些该类所实现的接口(间接实现也算)中没有在当前类中实现的方法。会被添加到vtable中,当然这个类一定是abtract的,不然必须要实现。
如果一个米兰达方法已经在父类的vtable中了,那么子类不需要新增,只需要修改就行了。


那么vtable中会有哪些方法呢?
当前类中声明的方法method,

如果method是被static修饰,那么一定不在。
如果被final修饰,那要看父类的vtable中有没有这个方法,有的话就把地址替换(因为子类会先复制父类的vtable)。没有的话,那么这个方法就不在vtable中,因为是这个类独有的,不会被覆盖,也就不需要转发。

从修饰符的角度来说private修饰的方法,一定在vtable中,而且是新增的。


查看一个类中的方法
有以下类

public class CW1 {

    @Override
    public String toString() {
        return super.toString();
    }

    public static void main(String[] args) throws IOException {
        ClassWriter cw=new ClassWriter(0);
        cw.visit(V1_8,ACC_PUBLIC+ACC_ABSTRACT+ACC_INTERFACE,
                "org/by/Cwtest",null,"java/lang/Object",
                null);
        cw.visitField(ACC_PUBLIC+ACC_STATIC+ACC_FINAL,"LESS","I",
                null,new Integer(-1)).visitEnd();
        cw.visitField(ACC_PUBLIC+ACC_STATIC+ACC_FINAL,"EQUAL","I",
                null,new Integer(0)).visitEnd();
        cw.visitField(ACC_PUBLIC+ACC_STATIC+ACC_FINAL,"GRATER","I",
                null,new Integer(1)).visitEnd();

        cw.visitMethod(ACC_PUBLIC+ACC_ABSTRACT,"compareTo","(Ljava/lang/Object;)I",
                null,null).visitEnd();

        cw.visitEnd();

        UdClassloader udClassloader=new UdClassloader();
        Class clz= udClassloader.defineClass("org.by.Cwtest",cw.toByteArray());

        System.out.println('1');

//        String systemRootUrl = (new File("")).toURI().toURL().getPath();
//        File file=new File(systemRootUrl+"org/by/Cwtest.class");
//        String parent=file.getParent();
//        File parent1=new File(parent);
//        parent1.mkdirs();
//        file.createNewFile();
//        FileOutputStream fileOutputStream=new FileOutputStream(file);
//        fileOutputStream.write(cw.toByteArray());
    }
}

打上断点,查看
在这里插入图片描述
查看类,
在这里插入图片描述
查看方法数组处的内存,可以看到当前类中定义了多少个方法,
第一个字节03可以直到,类中有3个方法,查看上面的类信息,可以知道分别是init,main,toString。也就是说当前类中定义的方法都会记录在这里
在这里插入图片描述

查看第一个方法init的字节码
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《Java虚拟机精讲》是一本介绍Java虚拟机的权威性书籍。该书的作者是豆瓣高分图书《深入理解Java虚拟机:JVM高级特性与最佳实践》的作者陈晓明。 该书共分为12章,全面解析Java虚拟机的原理、结构、内存管理、垃圾收集、安全性等方面的内容。 首先,该书通过介绍Java虚拟机的历史背景和发展,引出了Java虚拟机Java程序运行中的重要性以及其作用。 接着,该书详细介绍了Java虚拟机的架构,包括Java虚拟机的运行时数据区域、执行引擎、加载机制等。 然后,该书详细解析Java虚拟机的内存管理,包括Java堆、方法区、运行时常量池等各个内存区域的作用、实现原理以及优化技巧。 此外,该书还对Java虚拟机的垃圾收集机制进行深入介绍,包括垃圾收集算法、垃圾收集器的分和选择原则,并提供了相应的性能调优方法。 另外,该书还涵盖了Java虚拟机的安全性问题,包括如何保证Java程序的安全、如何进行代码审计等内容。 最后,该书还介绍了Java虚拟机的性能调优方法,包括如何进行性能诊断、性能优化等方面的内容。 总之,《Java虚拟机精讲》是一本全面而深入介绍Java虚拟机的书籍,对于想要深入了解Java虚拟机原理、优化Java程序性能的开发者和爱好者来说,是一本不可多得的好书。通过阅读该书,读者可以全面掌握Java虚拟机的工作原理,为编写高性能、稳定的Java程序提供指导。 ### 回答2: 《Java虚拟机精讲》是一本详细讲解Java虚拟机(JVM)原理和内部实现的电子书。这本书系统地介绍了JVM的结构、执行引擎、垃圾回收器、加载机制等关键组件。 首先,本书对JVM的结构进行了详细讲解。它将JVM分为加载子系统、执行引擎、内存管理和垃圾回收子系统等几个模块,每个模块都被详细讲解了其内部的工作原理和实现细节。读者可以通过对JVM结构的了解,深入理解JVM的内部运作方式。 其次,本书对JVM的执行引擎进行了深入解析。它介绍了JVM的运行时数据区域、栈帧的结构和数据传递方式等关键概念,帮助读者理解Java字节码的执行过程。 此外,垃圾回收是JVM的重要特性之一,而本书也对JVM的垃圾回收器进行了详细的介绍。它解释了不同型的垃圾回收器(如Serial、Parallel、CMS、G1等)的原理和适用场景,并且通过一些实例帮助读者理解垃圾回收器的工作过程。 最后,本书还对加载机制进行了解析。它包括加载的过程、双亲委派模型、的初始化和链接等内容,帮助读者深入理解Java的加载和运行机制。 总之,《Java虚拟机精讲》是一本深入讲解JVM原理和内部实现的电子书。通过阅读本书,读者可以更全面地了解JVM的结构、执行引擎、垃圾回收机制和加载机制等关键组成部分,为深入理解Java编程和性能优化提供了基础。 ### 回答3: 《Java虚拟机精讲PDF》是一本系统而全面地介绍Java虚拟机的教材。Java虚拟机(JVM)是Java程序在执行过程中所依赖的运行环境,它可以解释和执行Java字节码。此书围绕着JVM的运行机制、内存管理、垃圾回收等方面展开详细的介绍。 首先,该书详细讲解了JVM的执行过程和各个组件的作用。它深入探讨了加载器、字节码解释器、即时编译器等核心组件,并结合实例进行解释。读者能够学习到JVM执行Java代码的具体流程,了解JVM是如何加载并执行Java的。 其次,该书重点介绍了JVM的内存管理机制。它详细解释了Java内存模型、堆、栈、方法区等内存结构,以及垃圾回收机制。读者可以了解到Java虚拟机是如何管理和分配内存的,以及如何通过垃圾回收来优化内存的使用。 此外,该书还涵盖了性能调优、多线程并发等高级主题。它介绍了如何通过设置JVM参数来优化应用程序的性能,并提供了一些常见的性能调优技巧。同时,它也介绍了Java多线程编程的基本原理和常用的线程安全机制。 总的来说,《Java虚拟机精讲PDF》是一本对于深入了解和研究Java虚拟机非常有帮助的教材。通过阅读本书,读者可以系统全面地掌握Java虚拟机的原理和实现机制,从而更加高效地进行Java程序的开发和调优。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值