当前位置: 首页 > news >正文

JVM内存结构笔记03-方法区

文章目录

  • 方法区
    • 1.定义
    • 2.组成
      • 方法区与永久代和元空间的关系
      • 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?
    • 3.方法区常用参数
    • 4.运行时常量池
      • 查看class文件


在这里插入图片描述

方法区

1.定义

  • 方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。
  • 当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。
  • 方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

注意:在不同的虚拟机实现上,方法区的实现是不同的。

2.组成

在这里插入图片描述

  • 1.8之前的版本中,方法区用永久代来进行实现,永久代中包含类信息、运行时常量池。
  • 1.8之后永久代被废弃,变为在元空间中,元空间在本地内存(操作系统的内存)中。

方法区与永久代和元空间的关系

方法区和永久代以及元空间的关系很像 Java 中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是方法区,也就是说永久代以及元空间是 HotSpot 虚拟机对虚拟机规范中方法区的两种实现方式。并且,永久代是 JDK 1.8 之前的方法区实现,JDK 1.8 及以后方法区的实现变成了元空间。
在这里插入图片描述

为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?

在《深入理解 Java 虚拟机》第三版中解释到:
在这里插入图片描述
原因:

  1. 整个永久代有一个 JVM 本身设置的固定大小上限,无法进行调整(也就是受到 JVM 内存的限制),而元空间使用的是本地内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。
  2. 元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize 控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多了。
  3. 在 JDK8,合并 HotSpot 和 JRockit 的代码时, JRockit 从来没有一个叫永久代的东西, 合并之后就没有必要额外的设置这么一个永久代的地方了。
  4. 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

3.方法区常用参数

JDK 1.8 之前永久代还没被彻底移除的时候通常通过下面这些参数来调节方法区大小。

-XX:PermSize=N //方法区 (永久代) 初始大小
-XX:MaxPermSize=N //方法区 (永久代) 最大大小,超过这个值将会抛出 OutOfMemoryError 异常:java.lang.OutOfMemoryError: PermGen

相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。

JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是本地内存。下面是一些常用参数:

-XX:MetaspaceSize=N //设置 Metaspace 的初始(和最小大小)
-XX:MaxMetaspaceSize=N //设置 Metaspace 的最大大小

与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。

当元空间溢出时会得到如下错误:java.lang.OutOfMemoryError: MetaSpace
可以使用 -XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。
-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。

4.运行时常量池

运行时常量池是方法区的一部分。也就是说,当JVM加载一个类文件时,会将该类的常量池信息(常量池表)加载到方法区内的运行时常量池(内存)中。

在Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic Reference)的 常量池表(Constant Pool Table) 。

字面量:字面量是源代码中的固定值的表示法,即通过字面我们就能知道其值的含义。字面量包括整数、浮点数和字符串字面量。如:

String str = "hello";
final int num = 10;

其中 “hello” 和 10 就属于字面量,会存储在运行时常量池中。

符号引用:常见的符号引用包括类符号引用、字段符号引用、方法符号引用、接口方法符号。

《深入理解 Java 虚拟机》第三版 对符号引用和直接引用的解释如下:
在这里插入图片描述

  • 常量池表会在类加载后存放到方法区的运行时常量池中
  • 运行时常量池的功能类似于传统编程语言的符号表,尽管它包含了比典型符号表更广泛的数据。
  • 既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 错误。

查看class文件

代码

// 二进制字节码(类基本信息,常量池,类方法定义,包含了虚拟机指令)
public class HelloWorld {public static void main(String[] args) {System.out.println("hello world");}
}

通过反编译查看以上代码的.class文件

PS D:\Test\jvm\out\production\jvm\cn\qf> javap -v HelloWorld.class
Classfile /D:/Test/jvm/out/production/jvm/cn/qf/HelloWorld.classLast modified 2024-12-6; size 567 bytesMD5 checksum 8efebdac91aa496515fa1c161184e354Compiled from "HelloWorld.java"
public class cn.qf.HelloWorldminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #6.#20         // java/lang/Object."<init>":()V#2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;#3 = String             #23            // hello world#4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V#5 = Class              #26            // cn/qf//HelloWorld#6 = Class              #27            // java/lang/Object#7 = Utf8               <init>#8 = Utf8               ()V#9 = Utf8               Code#10 = Utf8               LineNumberTable#11 = Utf8               LocalVariableTable#12 = Utf8               this#13 = Utf8               Lcn/qf//HelloWorld;#14 = Utf8               main#15 = Utf8               ([Ljava/lang/String;)V#16 = Utf8               args#17 = Utf8               [Ljava/lang/String;#18 = Utf8               SourceFile#19 = Utf8               HelloWorld.java#20 = NameAndType        #7:#8          // "<init>":()V#21 = Class              #28            // java/lang/System#22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;#23 = Utf8               hello world#24 = Class              #31            // java/io/PrintStream#25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V#26 = Utf8               cn/qf/HelloWorld#27 = Utf8               java/lang/Object#28 = Utf8               java/lang/System#29 = Utf8               out#30 = Utf8               Ljava/io/PrintStream;#31 = Utf8               java/io/PrintStream#32 = Utf8               println#33 = Utf8               (Ljava/lang/String;)V
{public cn.qf.HelloWorld();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 4: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lcn/qf/HelloWorld;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #3                  // String hello world5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 6: 0line 7: 8LocalVariableTable:Start  Length  Slot  Name   Signature0       9     0  args   [Ljava/lang/String;
}
SourceFile: "HelloWorld.java"

翻到最下面
在这里插入图片描述
#2、#3、#4在Constant pool中查看定义
如#3在Constant pool对应#23

Constant pool:...#3 = String             #23            // hello world...#23 = Utf8               hello world...

常量池的作用:给指令提供常量符号,根据常量符合以查表的方式去找到他们。


相关文章:
JVM内存结构笔记01-运行时数据区域
JVM内存结构笔记02-堆
JVM内存结构笔记03-方法区
JVM内存结构笔记04-字符串常量池


http://www.mrgr.cn/news/94183.html

相关文章:

  • 18 | 实现简洁架构的 Handler 层
  • 系统开发资源
  • 51c自动驾驶~合集54
  • IEC61850标准下MMS 缓存报告控制块 ResvTms详细解析
  • JVM内存结构笔记04-字符串常量池
  • 15 | 定义简洁架构 Store 层的数据类型
  • CMD批处理一些冷门命令,编写windows脚本常用?
  • 10 | 基于 Gin 实现 HTTP 服务器
  • vue 仿deepseek前端开发一个对话界面
  • 如何搭建一个适配微信小程序,h5,app的uni-app项目
  • Go Ebiten小游戏开发:俄罗斯方块
  • halcon机器人视觉(四)calibrate_hand_eye_stationary_3d_sensor
  • JAVA 基础语法备忘录 -
  • 01 | Go 项目开发极速入门课介绍
  • 如何搭建一个适配微信小程序,h5,app的工程
  • VSCode集成C语言开发环境
  • 要登录的设备ip未知时的处理方法
  • 17 | 实现简洁架构的 Biz 层
  • 【大模型】WPS 接入 DeepSeek-R1详解,打造全能AI办公助手
  • 编程助手学Python--Deepseek对OpenAI的Python库调用GPT-4模型生成对话回复理解