<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


小结

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


相关文章
|
1天前
|
监控 算法 Java
使用Python的垃圾回收机制来管理内存
使用Python的垃圾回收机制来管理内存
|
18小时前
|
存储 Java 对象存储
jvm内存模型剖析
当线程cpu时间片执行完后,线程进入休眠状态,当再次唤醒时,通过程序计数器确定指令执行到哪一行,然后继续往下执行。
10 1
|
2天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
10 2
|
18小时前
|
存储 缓存 算法
JVM对象创建与内存分配机制
该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
5 0
|
23小时前
|
存储 安全 Java
JVM之内存结构
Java内存结构包括程序计数器、虚拟机栈、本地方法栈、堆和直接内存。程序计数器记录执行地址,线程私有,无溢出。虚拟机栈处理方法调用,局部变量在线程栈中,过深或过大可能导致StackOverflowError。本地方法栈服务于native方法。堆存储对象,线程共享,有垃圾回收。方法区存储类信息,1.8前的永久代,1.8后的元空间,溢出可调整相应参数。运行时常量池包含字符串池,1.6在永久代,1.8在堆,intern方法管理。直接内存用于NIO,提高读写性能,手动回收。
|
2天前
|
JavaScript 前端开发 算法
JavaScript 使用自动垃圾回收机制来管理内存
JavaScript 使用自动垃圾回收机制来管理内存
3 0
|
2天前
|
存储 Java C++
Java虚拟机(JVM)在执行Java程序时,会将其管理的内存划分为几个不同的区域
【6月更文挑战第24天】Java JVM管理内存分7区:程序计数器记录线程执行位置;虚拟机栈处理方法调用,每个线程有独立栈;本地方法栈服务native方法;Java堆存储所有对象实例,垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息;运行时常量池存储常量;直接内存不属于JVM规范,通过`java.nio`手动管理,不受GC直接影响。
14 5
|
6天前
|
监控 算法 Java
Java虚拟机(JVM)使用多种垃圾回收算法来管理内存,以确保程序运行时不会因为内存不足而崩溃。
【6月更文挑战第20天】Java JVM运用多种GC算法,如标记-清除、复制、标记-压缩、分代收集、增量收集、并行收集和并发标记,以自动化内存管理,防止因内存耗尽导致的程序崩溃。这些算法各有优劣,适应不同的性能和资源需求。垃圾回收旨在避免手动内存管理,简化编程。当遇到内存泄漏,可以借助VisualVM、JConsole或MAT等工具监测内存、生成堆转储,分析引用链并定位泄漏源,从而解决问题。
17 4
|
8天前
|
算法 Java
Java垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一种自动内存管理机制,用于在运行时自动回收不再使用的对象所占的内存空间
【6月更文挑战第18天】Java的GC自动回收内存,包括标记清除(产生碎片)、复制(效率低)、标记整理(兼顾连续性与效率)和分代收集(区分新生代和老年代,用不同算法优化)等策略。现代JVM通常采用分代收集,以平衡性能和内存利用率。
34 3
|
13天前
|
算法 安全 Java
JVM系列4-垃圾收集器与内存分配策略(二)
JVM系列4-垃圾收集器与内存分配策略(二)
20 0
JVM系列4-垃圾收集器与内存分配策略(二)