Java之JVM方法区、永久代、元空间、常量池简介说明
下文笔者讲述jvm中方法区,永久代,元空间,常量池的简介说明,如下所示
JVM内存模型简介
jvm堆是所有线程共享的 主要用来存储对象 堆: 年轻代和老年代两块区域 使用NewRatio参数来设定比例 对于年轻代,一个Eden区和两个Suvivor区,使用参数SuvivorRatio来设定大小; Java虚拟机栈/本地方法栈: 线程私有,主要存放局部变量表,操作数栈,动态链接和方法出口等 程序计数器——同样是线程私有,记录当前线程的行号指示器,为线程的切换提供保障 方法区——线程共享,主要存储类信息,常量池,静态变量,JIT编译后的代码等数据 方法区理论上来说是堆的逻辑组成部分: 运行时常量池——是方法区的一部分 用于存放编译期生成的各种字面量和符号引用
永久代和方法区的关系
永久代是HotSpot的概念 方法区是Java虚拟机规范中的定义, 是一种规范而永久代是一种实现 一个是标准一个是实现 其他的虚拟机实现并没有永久带这一说法 在1.7之前在(JDK1.2 ~ JDK6)的实现中 HotSpot使用永久代实现方法区 HotSpot使用 GC分代来实现方法区内存回收 可以使用如下参数来调节方法区的大小: -XX:PermSize 方法区初始大小 -XX:MaxPermSize 方法区最大大小 超过这个值将会抛出OutOfMemoryError异常 java.lang.OutOfMemoryError: PermGen
元空间
对于Java8 HotSpots取消永久代 方法区是一个规范,规范没变 它就一直在。那么取代永久代的就是元空间 元空间和永久代区别: 存储位置不同: 永久代物理是是堆的一部分 和新生代,老年代地址是连续的 元空间属于本地内存 存储内容不同: 元空间存储类的元信息 静态变量和常量池等并入堆中 相当于永久代的数据被分到了堆和元空间中。
Class文件常量池
Class 文件常量池指的是编译生成的 class 字节码文件 其结构中有一项是常量池(Constant Pool Table) 用于存放编译期生成的各种字面量和符号引用 这部分内容将在类加载后进入方法区的运行时常量池中存放 这里的字面量是指字符串字面量和声明为 final的(基本数据类型)常量值 这些字符串字面量除了类中所有双引号括起来的字符串(包括方法体内的) 还包括所有用到的类名、方法的名字和这些类与方法的字符串描述、字段(成员变量)的名称和描述符 声明为final的常量值指的是成员变量,不包含本地变量,本地变量是属于方法的 这些都在常量池的 UTF-8 表中(逻辑上的划分); 符号引用: 就是指指向 UTF-8 表中向这些字面量的引用 包括类和接口的全限定名(包括包路径的完整名) 字段的名称和描述符、方法的名称和描述符 只不过是以一组符号来描述所引用的目标,和内存并无关 所以称为符号引用,直接指向内存中某一地址的引用称为直接引用;
运行时常量池
运行时常量池是方法区的一部分 是一块内存区域 Class 文件常量池: 将在类加载后进入方法区的运行时常量池中存放 一个类加载到 JVM 中后对应一个运行时常量池 运行时常量池相对于 Class 文件常量池来说具备动态性 Class 文件常量只是一个静态存储结构 里面的引用都是符号引用 而运行时常量池可以在运行期间将符号引用解析为直接引用 可以说运行时常量池就是用来索引和查找字段和方法名称和描述符的 给定任意一个方法或字段的索引 通过这个索引最终可得到该方法或字段所属的类型信息和名称及描述符信息 这涉及到方法的调用和字段获取
字符串常量池
字符串常量池是全局,jvm只有一份 因此也称为全局字符串常量池 运行时常量池中的字符串字面量若是成员 则在类的加载初始化阶段就使用到了字符串常量池 若是本地,则在使用到的时候(执行此代码时)才会使用到字符串常量池 其实,“使用常量池”对应的字节码是一个 ldc 指令 在给 String 类型的引用赋值的时候会先执行这个指令 看常量池中是否存在这个字符串对象的引用 若有就直接返回这个引用 若没有,就在堆里创建这个字符串对象并在字符串常量池中记录下这个引用(jdk1.7) String 类的 intern() 方法还可在运行期间把字符串放到字符串常量池中 JVM 中除了字符串常量池 8种基本数据类型中除了两种浮点类型剩余的6种基本数据类型的包装类,都使用了缓冲池技术 但Byte、Short、Integer、Long、Character 这5种整型的包装类也只是在对应值在 [-128,127] 时才会使用缓冲池 超出此范围仍然会去创建新的对象
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。