Java Runtime Data Area | Java Debug 笔记

简介: Java Runtime Data Area | Java Debug 笔记

Java Runtime Data Area,即运行时数据区。一个 class 文件经过 ClassLoader 加载进入运行时数据区。


那么运行时数据区包含了什么信息呢?


Java Runtime Data Area 分布


整体分布



网络异常,图片无法展示
|


Program Counter Register



Program Counter Register 为每个线程独有。简称 PC 寄存器或者 PC 。用于记录当前线程执行到哪条指令。当线程被 CPU 调度执行的时候,会从当前线程的 PC 记录的指令编号继续执行。另外诸如分支、循环、跳转、异常等也都依赖于计数器。


JVM Stack



JVM Stack 为每个线程独有。JVM Stack 存放的是“栈帧”,每调用一次 Java 方法就会产生一个“栈帧”并推入 JVM Stack 中,方法结束后相应的“栈帧”弹栈。


由于“栈帧”对应具体的数据结构需要占用内存,所以当方法调用层级过高(例如:没有设置合理出口的递归调用),会发生“栈溢出”。


既然“栈帧”代表了一次方法调用,那么具体的数据结构装载些什么内容?一个完整的“栈帧”包含以下部分:


  1. 局部变量表:用于记录方法执行过程产生的局部变量。局部变量编号从 0 开始,对于一个实例方法而言,第 0 个局部变量为 this(注意不会有局部变量对应 super),再之后是方法的入参,最后才是方法开始执行才会有的局部变量。
  2. 操作栈:主要是存放执行指令所需要的参数和返回值。
  3. 动态链接:当该方法中需要引用一些局部变量表以外的数据时(例如 Method Area 中的类),需要有指向这些数据的链接。动态链接就是提供指向这些内容的链接。
  4. 返回值地址:当一个方法内部调用了别的方法时,需要暂停当前“栈帧”的执行,创建一个新的“栈帧”并执行,等待新“栈帧”执行完成,将结果放入当前“栈帧”的返回值地址处(通常为操作栈的栈顶),当前“栈帧”继续执行。


Native Method Stack



Native Method Stack 为每个线程独有。与 JVM Stack 类似,但 Native Method Stack 是服务与本地方法的(C/C++)栈结构,一般用于 JNI。


Heap



Heap 被所有线程共享。主要存放了通过 new 操作创建的对象。由于目前主流的垃圾回收器都是使用分代回收算法,所以堆内存通常也会被逻辑分区。


Heap 主要分为 Young 区(年轻代)和 Old 区(老年代),比例为 1 : 2。


其中 Young 区(年轻代)中分为一个 Eden 区 ,两个 Survivor 区(分别简称为 S0,S1),比例为 8 : 1 : 1。


关于 Heap 逻辑分区与垃圾回收的进一步介绍可以看:Java GC


Method Area



Method Area 是被所有线程共享的 Runtime Data Area。Method Area 是一个逻辑概念,是 JVM 所规范的。


Method Area 不像是栈或者堆有具体的数据结构对应。Method Area 在不同的 JDK 版本中实现有所不同:


  • 在 JDK 1.8 之前: Method Area 的实现是永久代(PermGen)。主要存放类信息、常量池、静态变量和编译好的代码。 永久代不会被 GC 回收,由于类信息都放在永久代,所以容易导致永久代内存溢出(在运行期间不断动态创建新类,永久代内容逐渐被占满)。 永久代必须指定大小,大小受 -XX:PermSize 和 -XX:MaxPermSize 参数影响,而这两个参数又受 JVM 的整体堆内存大小限制(在 JDK 1.7,永久代在堆空间中)。因此永久代实现的 Method Area 有较大的可能性发生内存溢出。
  • 在 JDK 1.8 之后:Method Area 的实现是元数据区(Metaspace)。 主要存放的内容和永久代一样,但是像类的元信息这些主要内容不再放在堆区,但是方法区中的静态变量和运行时常量池和 JDK 1.7 一样,仍在放在堆中。 元数据区可以不指定大小,也可以使用 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 参数指定,当不指定大小时,默认最大限制为物理内存大小(元数据区不在堆空间中,而是在本地内存中)。 由于元数据区的大小主要取决于物理内存大小,同时元数据区能够被 GC 回收,所以元数据区实现的 Method Area(相对于永久代实现的 Method Area )发生内存溢出的风险较低。


下图清楚展示了 JDK 1.7 & 1.8 堆空间划分的区别:


网络异常,图片无法展示
|


当同一个还没被加载的类被多个线程同时访问,这时只允许一个线程去加载它,另一个线程必须等待。


JDK 1.7 由于永久代在堆中,所以无论是 类的元信息、静态变量或者常量池都放在堆中,大小受限于堆内存,有较大的 OOM 风险。


JDK 1.8 取消了永久代,类的元信息等内容放在元空间中,元空间大小受限于物理内容。而静态变量和常量池则还是放在堆中。

相关文章
|
7天前
|
Java 开发工具 Android开发
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
本系列教程笔记详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。若需快速学习Kotlin,建议查看“简洁”系列教程。本期重点介绍了Kotlin与Java的共存方式,包括属性、单例对象、默认参数方法、包方法、扩展方法以及内部类和成员的互操作性。通过这些内容,帮助你在项目中更好地结合使用这两种语言。
18 1
|
8天前
|
Java 开发工具 Android开发
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
20 2
|
20天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
17天前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
20 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
7天前
|
Java 编译器 Android开发
Kotlin语法笔记(28) -Kotlin 与 Java 混编
本系列教程详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。对于希望快速学习Kotlin的用户,推荐查看“简洁”系列教程。本文档重点介绍了Kotlin与Java混编的技巧,包括代码转换、类调用、ProGuard问题、Android library开发建议以及在Kotlin和Java之间互相调用的方法。
12 1
|
7天前
|
安全 Java 编译器
Kotlin语法笔记(27) -Kotlin 与 Java 共存(二)
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。若需快速入门,建议查阅“简洁”系列教程。本文重点探讨Kotlin与Java共存的高级话题,包括属性访问、空安全、泛型处理、同步机制及SAM转换等,助你在项目中逐步引入Kotlin。
12 1
|
8天前
|
Java 编译器 Android开发
Kotlin语法笔记(28) -Kotlin 与 Java 混编
Kotlin语法笔记(28) -Kotlin 与 Java 混编
14 2
|
14天前
|
Java 数据库连接 编译器
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
|
17天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
本系列教程笔记详细讲解了Kotlin语法,适合希望深入了解Kotlin的开发者。对于需要快速学习Kotlin的小伙伴,推荐查看“简洁”系列教程。本篇笔记重点介绍了Kotlin与Java混编的技巧,包括代码转换、类调用、ProGuard问题、Android库开发建议以及相互调用时的注意事项。
19 3
|
22天前
|
存储 Java API
如何使用 Java 记录简化 Spring Data 中的数据实体
如何使用 Java 记录简化 Spring Data 中的数据实体
30 9