JVM运行时数据区(线程相关)

一、JVM运行时数据区
JVM运行时数据区是一个抽象概念,主要依赖于寄存器、高速缓存、主内存几个部分组成。
计算机运行 = 指令 + 数据,指令用于执行 方法, 数据 用于指向 存放的数据和对象。
虚拟机栈 --- 用于执行java方法
本地方法栈 --- 执行本地方法(通常时c语言实现的)
程序计数器 --- 用于对 执行程序的计数
java中的数据 --- 主要有 变量、常量、对象、数组等相关数据

1.1 线程私有的区域
程序计数器
占用内存空间较小,当前线程执行 java字节码的行号指示器;各线程之间独立存储,互不影响和干涉。
如果当前线程正在执行的是 java方法 ,则指明当前线程 执行的 代码字节码行数。
如果当前线程正在执行的是 native方法,则这个计数器值为空(undefined)。
此内存区域是唯一一个不会出现outofMemoryError情况的区域。

虚拟机栈
每个线程在运行时,在执行每个方法的时候都会打包成一个栈帧,存储了局部变量表,操作数栈,动态链接,方法出口等信息,然后放入栈。每个时刻正在执行的当前方法就是虚拟机栈顶的栈桢。方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程。
栈的大小缺省为 1M,可用jvm参数 –Xss 调整大小,例如-Xss256k

局部变量表:顾名思义就是局部变量的表,用于存放我们的局部变量的。
操作数据栈:存放我们方法执行的操作数的一个栈,遵循先进后出原则。
动态连接:因为有它,所以使 Java 语言具有多态特性
返回地址:
正常返回:(调用程序计数器中的地址作为返回)
三步曲:
1.恢复上层方法的局部变量表和操作数栈 为 默认值、
2.把返回值(如果有的话)压入调用者栈帧的操作数栈中、
3.调整 PC 计数器的值以指向方法调用指令后面的一条指令、

出现的异常:
线程请求的栈深度大于虚拟机所允许的深度:StackOverflowError
JVM 动态扩展时无法申请到足够的内存:OutOfMemoryError

本地方法栈
本地方法栈 native 方法调用JNI 到了底层的c/c++汇编语言,然后调用系统内核,驱动系统运转操作。

1.2 线程共享的区域

1.2.1 方法区/永久代/元空间
用于存储已经被虚拟机加载的类信息,常量("zdy","123"等),静态变量(static 变量)等数据
可用以下参数调整
jdk1.7 及以前:-XX:PermSize;-XX:MaxPermSize;
jdk1.8 以后称之为元空间:-XX:MetaspaceSize; -XX:MaxMetaspaceSize
元空间已经与jvm内存空间分离,即使用操作系统的内存。

类信息包括:
类的完整有效名称、返回类型、修饰符(public/private/protected...)、变量名、方法名、方法代码、这个类的直接父类的完整有效名、类的直接接口的有序列表。

运行时常量池
1.符号引用
比如 类加载器装载 一个类时,此时可以通过虚拟机获取这个类的实际内存地址,再通过该类的全限定名 作为引用符号来替换这个类的实际内存地址,在jvm编译成字节码时,用引用符号来来替换引用地址,再在加载类时再通过虚拟机获取该引用符号指向引用内的实际内存地址。

2.字面量
文本字符串 String a = "abc",这个 abc 就是字面量
八种基本类型 int a = 1; 这个 1 就是字面量
说白了就是引用符号的值。

1.2.2 堆
几乎所有对象都分配在这里,也是垃圾回收发生的主要区域
可用以下参数调整:
-Xms:堆的最小值;
-Xmx:堆的最大值;
-Xmn:新生代的大小;
-XX:NewSize;新生代最小值;
-XX:MaxNewSize:新生代最大值;
例如- Xmx256m

新生代(eden 内存默认与幸存区比 为 8) 幸存区(From/To 1:1)
主要涉及 复制-清除算法,垃圾回收名称Minor GC
JVM第一次GC时,只会触发 Eden区复制到From区,随后可能会发生Eden区或者From区内存不足,导致再次触发MinorGC。
JVM第二次GC及以后,Eden区与From区中的存活对象复制到To区,并且年龄+1,之后回收掉Eden与From区所有对象,再将当前To区进行角色切换为From区。
当From区中的对象年龄达到 默认值(可通过-XX:MaxTenuringThreshold修改) 15岁后,会将该对象存入老年代,或者From区中的内存已满时,jvm内存担保机制,会将对象直接放入老年代。

老年代(Old)
主要涉及 标记-清除,复制-整理算法,垃圾回收名称Old GC
触发条件,minor GC后,老年代空间不足时,会触发Major GC或Old GC,Major GC通常与Full GC是等价的
Full GC至少会触发一次Minor GC和Major GC,会对堆内的所有内存进行回收,其中包括新生代、老年代、永久代(元空间)

老年代会对对象头做GC 标记,随后清除未标记的对象并将存货对象压缩到内存边界,使片状的的内存连续化。

1.2.3 直接内存
直接内存也属于线程共享的区域。
使用 Native 函数库直接分配堆外内存(NIO)
并不是 JVM 运行时数据区域的一部分,但是会被频繁使用(可以通过-XX:MaxDirectMemorySize 来设置(不设置的话默认与堆内存最大值
一样,也会出现 OOM 异常)
使用直接内存避免了在 Java 堆和 Native 堆中来回复制数据,能够提高效率
测试用例 JavaStack:设置 JVM 参数-Xmx100m,运行异常,因为如果没设置-XX:MaxDirectMemorySize,则默认与-Xmx 参数值相同为 100M,
分配 128M 直接内存超出限制范围

站在线程角度来看
线程私有区域:虚拟机栈、本地方法栈、程序计数器三个区域的生命周期和线程相同。
线程共享区域:涉及到生命周期管理和垃圾回收等概念

posted @   vello  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示