jvm内存区域

内存区域

1. 运行时数据区概述

1.1 线程私有区域

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈

1.2 所有线程共享区域

  • 方法区

2. 程序计数器

  • 存储指向下一条指令的地址,即将要执行的指令代码由执行引擎读取下一条指令
  • 唯一一个没有内存问题的区域。

2.1 为什么使用PC寄存器?

  • CPU需要不停的切换各个线程,需要明确切换回来后继续执行的位置
  • JVM的字节码解释器需要通过改变pc寄存器的值来明确下一条应该执行的字节码指令

2.2 并行VS并发

  • 并行:同一时刻一组程序独立异步地执行 vs 串行
  • 并发:在同一时间段内,两个或多个程序执行,有时间上的重叠(宏观上同时,微观上仍是顺序执行)

3. Java虚拟机栈

虚拟机栈描述Java方法执行的内存模型,每个线程在创建时都会创建一个虚拟机栈(生命周期同线程一样),其内部保存一个个的栈帧(Stack Frame),每个方法在执行的同时都会创建一个栈帧。

此区域不存在垃圾回收。

3.1 栈帧内部结构

在编译代码时期,栈帧中局部变量表、操作数栈深度都已经确定了,并写入到方法表的Code属性中,因此一个栈帧需要分配多少内存不会受到程序运行期变量数据的影响。

  • 局部变量表(Local Variables) —— 方法运行期间不会改变局部变量表的大小
  • 操作数栈(Operand Stack)
  • 动态链接(Dynamic Linking) (或指向运行时常量池的方法引用)
  • 方法返回地址(Return Address) (或方法正常退出或异常退出的定义)
  • 附加信息

3.2 局部变量表

  • 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量
    • 8种基本数据类型
    • 对象引用——reference
    • returnAddress
  • 局部变量表建立在线程的栈上,是线程私有数据,不存在数据安全问题
  • 局部变量表所需的容量大小在编译期间完成分配,并保存在方法的Code属性的maximum local variables数据项中。在方法运行期间不会改变局部变量表的大小
  • 最基本的存储单元Slot——变量槽
    • 32位以内的类型占用一个slot(包括returnAddress类型)
    • 64位类型占用2个slot(局部变量空间)
    • slot重复利用:一个局部变量过了其作用域,在其作用域之后声明的新变量可以复用其槽位
  • 局部变量表中的变量也是重要的垃圾回收根节点,被局部变量表直接或间接引用的对象都不会被回收
  • 变量初始化
    • 类变量:①准备阶段赋予系统初始值 ②初始化赋予构造方法中的初始值
    • 局部变量:使用之前必须进行初始化(否则编译阶段报错、或字节码校验异常导致类加载失败)
Start PC Length Index Name Descriptor
0 7 0 this Lcom/djtu/test/ClassName;
3 4 1 age I
6 1 2 name Ljava/lang/String;
索引 类型 参数
0 int int a
1 long long b
3 float float c
4 double double d
6 reference Object n
...

3.3 操作数栈

可以使用数组或链表实现

  • 在方法的执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈(push)/出栈(pop)
    • 主要用于保存计算过程的中间结果,同时作为计算过程中变量的临时存储空间
    • 每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义好了,保存在方法的code属性中——max_stack
  • Java虚拟机的解释引擎是基于栈的执行引擎,栈——操作数栈
  • 32位数据类型所占栈容量为1
  • 64位类型所占栈容量为2
  • 栈帧部分重叠:在进行方法调用时可以共用一部分数据


3.4 动态链接

方法调用

虚方法与非虚方法

  • 非虚方法:如果方法在编译期间就确定了具体的调用版本,这个版本在运行时是不可变的这样的方法称为非虚方法:静态方法私有方法final方法实例构造器父类方法都是非虚方法
  • 其他方法称为虚方法

动态类型语言和静态类型语言

  • 静态类型语言:判断变量自身的类型信息(Java)
  • 动态类型语言:判断变量值的类型信息,变量没有类型信息,变量值才有类型信息(JavaScript)

3.5 方法返回地址

  • 存放调用该方法的pc寄存器的值
  • 一个方法的结束方式:
    • 正常执行完成:调用者的pc寄存器的值作为返回地址,即调用该方法的指令的下一条指令地址
    • 出现未处理异常、非正常退出:返回地址通过异常表来确定,栈帧中一般不会保存这部分信息

方法的结束方式——栈帧被弹出

  • 正常返回,使用return指令
  • 抛出异常

3.6 异常

  • 栈设置固定大小:线程请求的栈深度大于虚拟机允许的深度 StackOverflowError
  • 栈大小可动态扩展:无法申请到足够的内存 OutOfMemoryError

4. 本地方法栈

与虚拟机栈作用类似,本地方法栈为虚拟机使用的Native方法服务。

异常:StackOverflowError OutOfMemoryError

5. Java堆

Java堆是被所有线程共享的一块内存区域,垃圾收集器管理的主要区域(GC堆)

  • 每个JVM实例 -> 堆空间
  • The heap is the runtime data area from which memory for all/almost class instance and array is allocated

堆内存划分

  • 基于内存回收角度
    • 新生代——Eden空间、From Survivor空间、To Survivor空间
    • 老年代
    • 永久代JDK7/元空间JDK8
  • 基于内存分配角度
    • 线程共享的Java堆中可以划分出多个线程私有的分配缓冲区(TLAB)

设置堆空间大小的参数

  • -Xms = -XX:InitialHeapSize 用来设置堆空间(年轻代+老年代)的初始内存大小
    • X:是jvm的运行参数
    • ms:memory start
  • -Xmx 用来设置堆空间(年轻代+老年代)的最大内存大小
  • 默认情况
    • 初始内存大小:物理内存大小/64
    • 最大内存大小:物理内存/4
  • 设置年轻代与老年代比例(一般不修改):-XX:NewRatio
    • 默认:-XX:NewRatio=2 新生代:老年代=1:2
    • 修改:-XX:NewRatio=4 新生代:老年代=1:4
  • 设置新生代中 Eden区 Survivor区 的比例:-XX:SurvivorRatio(默认-XX:SurvivorRatio=8
    • HotSpot虚拟机,默认Eden:S0:S1=8:1:1
  • 关闭自适应的内存分配策略:-XX:-UseAdaptiveSizePolicy
public class HeapSpaceInitial {

    public static void main(String[] args) {
        long initialMemory = Runtime.getRuntime().totalMemory()/1024/1024;
        long maxMemory = Runtime.getRuntime().maxMemory()/1024/1024;

        System.out.println("-Xms:" + initialMemory + "m");
        System.out.println("-Xmx:" + maxMemory + "m");

        System.out.println("系统内存大小:" + initialMemory*64.0/1024 + "G");
        System.out.println("系统内存大小:" + maxMemory*4.0/1024 + "G");
    }
}

//设置jvm启动参数:`-Xms600m -Xmx600m`
//输出:
-Xms:575m
-Xmx:575m

查看GC

  • 命令行方式
    • jps查询当前正在执行的java进程
    • jstat -gc pid查看gc信息
  • 设置jvm启动参数:-XX:+PrintGCDetails
    eg:-Xms600m -Xmx600m -XX:+PrintGCDetails

异常OutOfMemoryError

6. 方法区

所有线程共享的一块内存区域,用于存储已被虚拟机加载的——类信息常量静态变量即时编译器编译后的代码等数据。

  • 1.6:堆内存的一部分
  • 1.8:直接内存(元数据区——Native Memory)

回收内容:针对常量池的回收和对类型的卸载

异常:OutOfMemoryError

posted @   zwbsoft  阅读(42)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示