JVM方法区

概述

  • 线程间是共享的
  • 可以是固定大小或动态扩展大小. 如果系统定义了太多的类, 导致空间不足, 则会抛出内存溢出 OOM
  • 虽然方法区在运行时数据区属独立的存在, 但逻辑上属于堆空间

版本之演变历程

  • Jdk6或之前的内部实现是永久代(Permanent Space), 静态变量和字符串常量池(StringTable)存放在永久代上, 不过通过 new关键字实例化的静态变量是会存到堆空间
  • Jdk7的内部实现依然是永久代, 但是静态变量和字符串常量池移至堆空间
  • Jdk8或之后的内部实现是元空间(Meta Space), 静态变量和字符串常量池依然保存在堆空间中

元空间与永久代的区别

  1. 元空间用的本地物理内存, 所以容量上限为本地物理内存大小, 而永久代是虚拟机分配的内存
  2. 元空间相比永久代, 发生 OOM的概率少, 且由于是方法区的原因引起的 Full GC也相对少很多
  3. 当 OOM时抛出不同的信息, 元空间: OutOfMemoryError: Metaspace, 永久代: OutOfMemoryError: PermGen space 堆的溢出信息是 OutOfMemoryError: Java heap space
  4. 调试参数:
  • 永久代: -XX:PermSize(设置初始空间, 默认值是20.75m). -XX:MaxPermSize(设置最大可分配空间, 32位机器默认值是64m, 64位机器默认值是82m)
  • 元空间: -XX:MetaspaceSize(设置初始空间, 默认值依赖系统大约21m). -XX:MaxMetaspaceSize(设置最大可分配空间, 默认值是-1即没有限制)

内部结构

  • 主要存放类型信息(类信息, 域信息, 方法信息), 运行时常量池, 静态变量, 字符串常量池和 JIT编译后的代码缓存(CodeCache)
  • Jdk7开始静态变量和字符串常量池移至堆空间

类型信息

  • 类信息(class, interface, enum, annotation), JVM在方法区中保存以下类型信息:
  1. 类名称(含包名)
  2. 直接父类的完整有效名称(对于 interface或 java.lang.Object没有父类)
  3. 类型的修饰符(public, abstract, final的某个子集)
  4. 类型直接接口的一个有序列表(接口的代码顺序)
  • 域信息(Field, 成员属性), JVM保存类型的所有域的相关信息(包括域的声明顺序):
  1. 域名称
  2. 域类型
  3. 域修饰符(public,private,protected,static,final,volatile,transient的某个子集)
  • 方法信息(Method), JVM保存所有方法的以下信息(包括方法的声明顺序):
  1. 方法名称
  2. 方法的返回类型(或void)
  3. 方法参数的数量和类型(有序的)
  4. 方法的修饰符(public, private, protected, static, final, synchronized, native, abstract的一个子集)
  5. 方法的字节码(bytecodes)和操作数栈, 方法栈帧(保存以栈帧为单位的线程状态)的局部变量表及大小(native, abstract方法除外)
  6. 异常表: 每个异常处理的开始位置, 结束位置, 代码处理在程序计数器中的偏移地址, 被捕获的异常类的常量池索引(native, abstract方法除外)

常量池(Constant Pool Table)

  • 字节码文件占大部分内容的是常量池
  • 常量池可以看作是一张表,虚拟机根据这张表找到要执行的类名, 方法名, 参数类型, 字面量等类型
  • 字节码文件的常量池加载到方法区后叫做运行时常量池(Runtime Constant Pool)
  • 使用常量池能节省很多空间, 以此提升性能
  • 运行时常量池相比字节码文件中的常量池, 具备了动态性
  • 常量池主要有两大类: 字面量和符号引用

运行时常量池(Runtime Constant Pool)

  • 在类加载完成后, 将每个 .class常量池中的符号引用转换为直接引用

字符串常量池(StringTable)

  • Jdk6或之前的版本中, 由于永久代的回收效率很低, 导致字符串常量池的回收效率不高, 而实际开发中会有大量的字符串被创建或回收, 因此永久代内存空间容易被占满. 所以从 Jdk7开始移到堆空间中来改善了垃圾回收效率

方法区的垃圾回收

  • 主要回收运行时常量池和类型信息(指定类的所有实例都已被回收的类, 加载该类的类加载器已被回收, 该类对应的java.lang.Class对象没有在任何地方被引用, 无法在任何地方通过反射访问该类的方法)

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

猜你喜欢

转载自blog.csdn.net/qcl108/article/details/108718758
今日推荐