JVM之【运行时数据区2——方法区(元空间)】

方法区(元空间)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在Java虚拟机(JVM)中,方法区(Method Area)是内存的一个逻辑部分,主要用于存储类信息、常量、静态变量、即时编译器编译后的代码等。方法区是线程共享的区域。根据Java虚拟机规范,方法区是堆的一部分,但在实际的JVM实现中(如HotSpot虚拟机),方法区有时也被称为“永久代”(Permanent Generation),在Java 8及以后,永久代被移除,取而代之的是元空间(Metaspace)。

永久代(Permanent Generation)和元空间(Metaspace)

方法区中存储的数据类型

  1. 类型信息:个人以为,这里其实就是class的字节码被存储到方法区中而已。字节码中的信息,这里都有。

    1. 类的全限定名:如java.lang.String
    2. 父类的全限定名:如java.lang.Object
    3. 类加载器引用:加载该类的类加载器的引用。
    4. 访问修饰符:如publicabstractfinal等。
    5. 接口:该类实现的所有接口的列表。
    6. 字段信息:类中声明的所有字段,包括字段名、类型、访问修饰符等。
    7. 方法信息:类中声明的所有方法,包括方法名、返回类型、参数类型、访问修饰符、方法字节码等。
    8. 异常表
    9. 字段元数据:字段的详细信息,可以通过反射获取和操作,如字段名、类型、访问修饰符等。
    10. 方法元数据:方法的详细信息,包括方法名、参数类型、返回类型、访问修饰符等,这些信息通过反射API可以获取。
    11. 注解信息:类、字段、方法等上面声明的注解信息。
    12. 类加载器信息:与类加载器相关的信息,用于管理和跟踪类的加载。
    13. 类的初始化状态:类是否已经被初始化,静态初始化块是否已经执行等。
      静态变量和类关联在一起,随着类加载而加载。类变量(静态变量)被类的所有实例所共享,即使没有类实例,也可以访问它
  2. 运行时常量池:每个类或接口的常量池,包含编译期生成的各种字面量和符号引用。常量池主要用于存放编译器生成的各种字面量和符号引用,这些数据将在类加载和运行时使用。

  3. 静态变量:类的静态字段。在类加载的时候,这些字段就被分配在方法区中,而不是实例对象的堆空间中。

  4. 即时编译期编译后的缓存代码:JIT(Just-In-Time)编译器编译后的本地代码。

这些数据的作用

  1. 类信息:用于支持类的加载、链接和初始化过程。JVM需要这些信息来识别和执行类的方法和字段,以及进行动态分派。

  2. 运行时常量池:为类的字面量和符号引用提供存储,支持动态链接和运行时解析。常量池中的数据在类加载时会被解析为直接引用,帮助JVM进行方法调用、字段访问等操作。

  3. 静态变量:静态变量属于类而不是类的实例,每个类只存在一份静态变量。它们用于存储类级别的共享数据,可以在不创建实例的情况下通过类本身进行访问。

  4. 即时编译后的代码:提升运行效率。JIT编译器将字节码转换为机器码,存储在方法区中,直接执行以提升性能。

  5. 类的元数据信息:帮助JVM管理类的生命周期,包括类的加载、链接、初始化和卸载。

总结

方法区在JVM中是一个非常重要的内存区域,它存储了类的元数据、运行时常量池、静态变量和即时编译后的代码等关键数据。这些数据为JVM运行Java程序提供了必要的支持,确保类的正确加载、链接和初始化,并提高程序的执行效率。


个人以为,运行时常量池,就是ClassFile文件里面的常量池加载到方法去中。

  1. 运行时常量池(Runtime constantPool)是方法区的一部分。常量池表(constant Pool Table)是class文件的一部分,用于存放编译期生成的名种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
  2. 运行时常量池,在加载类和接口到虚拟机后,就会创建对应的运行时常量池。JVM为每个已加载的类型(类或接口)都维护一个常量池。池中的数据项像数组项一样是通过索引访问的。
  3. 运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期解析后才能够获得的方法或者字段引用。此时不再是常量池中的符号地址了,这里换为真实地址。
  4. 运行时常量池,相对于class文件常量池的另一重要特征是:具备动态性>String.intern()
  5. 运行时常量池类似于传统编程语言中的符号表(symbol table),但是它所包含的数据却比符号表要更加丰富一些。
  6. 当创建类或接口的运行时常量池时,如果构造运行时常量池所需的内存空间超过了方法区所能提供的最大值,则JVM会抛OutofMemoryError异常。

方法区在内存结构的哪里(方法区的演进)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 随着Java8的到来,HotSpot VM中再也见不到永久代了。但是这并不意味着类的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域,这个区域叫做元空间(Metaspace)。
  • 由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。

这项改动是很有必要的,原因有:

  • 为永久代设置空间大小是很难确定的。在某些场景下,如果动态加载类过多,容易产生Perm区的O0M。比如某个实际web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误
    "Exception in thread 'dubbo client x.x connector’java.ang.OutOfMemoryError: PermGenspace”
  • 元空间和永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
  • 对永久代进行调优是很困难的。

StringTable为什么要调整?
jdk7中将stringTable放到了堆空间中。

  • 因为永久代的回收效率很低,在full gc的时候才会触发。而full gc是老年代的空间不足、永久代不足时才会触发。这就导致stringTable回收效率不高。
  • 而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存。

静态变量在方法区中,方法区是元空间。应该如何理解在 JVM 1.8 中,有些人说静态变量存储在堆中?

静态变量的存储位置

  1. 元空间(Metaspace):
    JVM 1.8 引入了元空间(Metaspace),用于替代之前版本的永久代(PermGen)。元空间存储类的元数据,包括类的结构信息、方法元数据、常量池等。然而,静态变量并不直接存储在元空间中。

  2. 堆内存(Heap Memory):
    静态变量在 JVM 1.8 中确实涉及堆内存的使用。具体而言,静态变量的实际值(对象)是存储在堆内存中的。元空间中的类元数据包含指向这些堆内存对象的引用。因此,可以说静态变量的引用存在于元空间,而它们所引用的对象存在于堆内存中。

理解堆内存中的静态变量

  • 引用和对象分离:
    静态变量的声明和引用是类的一部分,存储在元空间中。但如果静态变量是一个对象引用,这个引用指向的对象实际存储在堆内存中。例如:

    public class MyClass {
        public static String myStaticVar = "Hello, World!";
    }
    

    在这种情况下,myStaticVar 引用存储在元空间中,但实际的字符串对象 "Hello, World!" 存储在堆内存中。

  • 原始类型和对象类型:
    对于原始类型的静态变量(如 intboolean 等),它们的值直接存储在元空间中,因为这些值是固定大小的,且不需要额外的引用。

    对于对象类型的静态变量(如上例中的 String),引用存储在元空间中,而对象本身存储在堆内存中。这解释了为什么有人说静态变量在堆中,因为对象实际存储在堆中。

示例说明

public class Example {
    public static int staticInt = 42;
    public static String staticString = "Hello";
}

在上述例子中:

  • staticInt 的值 42 直接存储在元空间中。
  • staticString 的引用存储在元空间中,但 "Hello" 字符串对象存储在堆内存中。

总结

在 JVM 1.8 中,静态变量的引用存储在元空间中,但这些引用指向的对象(如果是对象类型)存储在堆内存中。因此,静态变量与堆内存有密切关系,特别是对于引用类型的静态变量。这种理解有助于解释为什么有人会说静态变量在堆中。静态变量的引用和它们所指向的对象分离存储,引用在元空间,对象在堆内存中。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值