<JVM上篇:内存与垃圾回收篇>07-方法区(二)

简介: <JVM上篇:内存与垃圾回收篇>07-方法区

7.4.2. 方法区的内部结构

类型信息

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


这个类型的完整有效名称(全名=包名.类名)

这个类型直接父类的完整有效名(对于 interface 或是 java.lang.object,都没有父类)

这个类型的修饰符(public,abstract,final 的某个子集)

这个类型直接接口的一个有序列表


9daab361ee0f00e4141c6041e8a17165.png


域(Field)信息


JVM 必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。


域的相关信息包括:域名称、域类型、域修饰符(public,private,protected,static,final,volatile,transient 的某个子集)


7c5b2ecab13ee12ef4cb45958ea28c85.png


问题:方法区包含域信息,也就是包含成员变量?


只是存放了成员变量的信息,具体的值会和对象一起放在堆中。类变量会存放在方法区


方法(Method)信息


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


方法名称

方法的返回类型(或 void)

方法参数的数量和类型(按顺序)

方法的修饰符(public,private,protected,static,final,synchronized,native,abstract 的一个子集)

方法的字节码(bytecodes)、操作数栈、局部变量表及大小(abstract 和 native 方法除外)

异常表(abstract 和 native 方法除外)

每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引


5519916b9b53cf33f24cae34e5778dda.png


**备注:**以上代码皆来源于MethodInnerStrucTest 类的字节码文件, 可以通过 javap -v -p Met hodInnerStrucTest.class > test.txt 命令,将字节码导出到文件中,便于观察.


字节码文件中存放着类文件的类型详细信息.(如上所示)


类加载器将字节码文件加载到方法区后. 方法区中除了记录着类的详细信息,还包括该类是被哪个类加载器加载的等, 同时类加载器(也是类)也会记录加载了哪些类.


non-final 的类变量


静态变量和类关联在一起,随着类的加载而加载,他们成为类数据在逻辑上的一部分

类变量被类的所有实例共享,即使没有类实例时,你也可以访问它


/**
 * non-final的类变量
 * @author shkstart  shkstart@126.com
 * @create 2020  20:37
 */
public class MethodAreaTest {
    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!");
    }
}

执行以上的main方法,并不会报错,amazing!

补充说明:全局常量(static final)

被声明为 final 的类变量的处理方法则不同,每个全局常量在编译的时候就会被分配了。

通过字节码查看

代码同上面....


对比final修饰的变量赋值操作


5e741721c6a336d6addc362d1cf609af.png


查看静态变量赋值操作


dfc2141db974bd65aca7c552ea5fe57a.png


7.4.3. 运行时常量池 VS 常量池


7ec9b450764c623c89e7de7e0d3c8d19.png


方法区,内部包含了运行时常量池

字节码文件,内部包含了常量池

要弄清楚方法区,需要理解清楚 ClassFile,因为加载类的信息都在方法区。

要弄清楚方法区的运行时常量池,需要理解清楚 ClassFile 中的常量池。

官方文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html


be6d675673b353a266d48df43b0411f4.png


一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述符信息外,还包含一项信息就是常量池表(Constant Pool Table),包括各种字面量和对类型、域和方法的符号引用. 也就是说一个方法的具体实现细节都藏在常量池中


53d9947cd285beea95ad2bec8c843f0d.png


举例:


常量池中的信息相当于炒菜的基本原料,每个方法相当于一道道菜.每道菜都可能会用到那些基本原材料.对应到代码中,方法的字节码会用到常量池中的内容


为什么需要常量池?


一个 java 源文件中的类、接口,编译后产生一个字节码文件。而 Java 中的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,换另一种方式,可以存到常量池,这个字节码包含了指向常量池的引用。在动态链接的时候会用到运行时常量池,之前有介绍。


比如:如下的代码:

public class SimpleClass {
    public void sayHello() {
        System.out.println("hello");
    }
}

虽然只有 194 字节,但是里面却使用了 String、System、PrintStream 及 Object 等结构。这里的代码量其实很少了,如果代码多的话,引用的结构将会更多,这里就需要用到常量池了。


0842d969095f4c40f3187245702b880c.png


常量池中有什么?

击中常量池内存储的数据类型包括:


数量值

字符串值

类引用

字段引用

方法引用


b83400b9b16e5cd29e53a51b0de945e4.png


例如下面这段代码:

public class MethodAreaTest2 {
    public static void main(String args[]) {
        Object obj = new Object();
    }
}

Object obj = new Object();将会被翻译成如下字节码:

0: new #2  // Class java/lang/Object
1: dup
2: invokespecial // Method java/lang/Object "<init>"() V


小结

常量池、可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型


相关文章
|
4天前
|
存储 监控 算法
【JVM】如何定位、解决内存泄漏和溢出
【JVM】如何定位、解决内存泄漏和溢出
12 0
|
4天前
|
算法 安全 Java
JVM系列4-垃圾收集器与内存分配策略(二)
JVM系列4-垃圾收集器与内存分配策略(二)
10 0
JVM系列4-垃圾收集器与内存分配策略(二)
|
4天前
|
存储 监控 算法
JVM系列4-垃圾收集器与内存分配策略(一)
JVM系列4-垃圾收集器与内存分配策略(一)
14 0
|
9天前
|
Java
JDK8中JVM堆内存划分
JDK8中JVM堆内存划分
11 0
|
10天前
|
Java 数据库连接 图形学
JVM内存泄漏检测与处理
JVM内存泄漏检测与处理
9 0
|
10天前
|
存储 机器学习/深度学习 Java
探索JVM 内存分配
探索JVM 内存分配
9 0
|
10天前
|
存储 Java 编译器
jvm内存结构
jvm内存结构
21 0
|
18天前
|
Java Linux
JVM堆内存诊断
JVM堆内存诊断
18 0
|
23天前
|
存储 Java 对象存储
JVM(内存区域划分)
JVM(内存区域划分)
33 1
|
1月前
|
存储 缓存 算法
深入浅出JVM(二)之运行时数据区和内存溢出异常
深入浅出JVM(二)之运行时数据区和内存溢出异常