说下你对方法区演变过程和内部结构的理解(中)

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

上文中我们对方法区的实现永久代和元空间已经有了基本的认识,本文我们继续深入,来聊一下它的内部结构都有些什么法宝!

内部结构

《深入理解Java虚拟机》书中对方法区存储内容描述如下:它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。接下来我们就一起来看一下它的内部结构。

类型信息

对每个加载的类型( 类 class、接口 interface、枚举 enum、注解 annotation),JVM 必须在方法区中存储以下类型信息:

  1. 这个类型的完整有效名称(全名=包名.类名)
  2. 这个类型直接父类的完整有效名(对于 interface 或是 java. lang.Object ,都没有父类)
  3. 这个类型的修饰符( public , abstract, final 的某个子集)
  4. 这个类型直接接口的一个有序列表

域(Field)信息

  • JVM必须在方法区中保存类型的所有域(field,也称为属性)的相关信息以及域的声明顺序;
  • 域的相关信息包括:域名称、 域类型、域修饰符(public, private,protected, static, final, volatile, transient 的某个子集)

方法(Method)信息

JVM 必须保存所有方法的以下信息,同域信息一样包括声明顺序:

  • 方法名称
  • 方法的返回类型(或void)
  • 方法参数的数量和类型(按顺序)
  • 方法的修饰符(public, private, protected, static, final,synchronized, native , abstract 的一个子集)
  • 方法的字节码(bytecodes)、操作数栈、局部变量表及大小( abstract 和 native 方法除外)
  • 异常表( abstract 和 native 方法除外)每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引

non-final 的类变量

  • 静态变量和类关联在一起,随着类的加载而加载,他们成为类数据在逻辑上的一部分
  • 类变量被类的所有实例所共享,即使没有类实例你也可以访问它。

我们可以通过例子来查看:

public class MethodAreaDemo2 {
    public static void main(String[] args) {
        Order order = null;
        order.hello();
        System.out.println(order.count);
    }
}

class Order {
    public static int count = 1;
    public static final int number = 2;

    public static void hello() {
        System.out.println("hello!");
    }
}
复制代码

运行结果为:

hello!
1
复制代码

可以打开 IDEA 的 Terminal 窗口,在 MethodAreaDemo2.class 所在的路径下,输入 javap -v -p MethodAreaDemo2.class 命令

通过图片我们可以看出被声明为 final 的类变量的处理方法是不一样的,全局常量在编译的时候就被分配了。

运行时常量池

说到运行时常量池,我们先来了解一下什么是常量池表。

常量池表

一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述信息外,还包含一项信息那就是常量池表(Constant Pool Table),里边存储着数量值字符串值类引用字段引用方法引用

为什么字节码文件需要常量池?

java 源文件中的类、接口,编译后会产生一个字节码文件。而字节码文件需要数据支持,通常这种数据会很大,以至于不能直接存放到字节码中。换一种方式,可以将指向这些数据的符号引用存到字节码文件的常量池中,这样字节码只需使用常量池就可以在运行时通过动态链接找到相应的数据并使用。

运行时常量池

运行时常量池( Runtime Constant Pool)是方法区的一部分,类加载器加载字节码文件时,将常量池表加载进方法区的运行时常量池。运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期解析后才能够获得的方法或者字段引用。此时不再是常量池中的符号地址了,这里换为真实地址。

运行时常量池,相对于 Class 文件常量池的另一重要特征是:具备动态性,比如 String.intern()

下文我们将讨论一下 Hotspot 虚拟机的演进细节!如果你有不同的意见或者更好的idea,欢迎联系阿Q,添加阿Q可以加入技术交流群参与讨论呦!

猜你喜欢

转载自juejin.im/post/7018847034962608165