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

【JVM】JVM内存结构

【JVM】彻底搞懂JVM内存结构:对象、方法、变量都存在哪?

    • 一、JVM内存的核心划分
    • 二、线程私有
      • 1. 虚拟机栈
      • 2. 本地方法栈
      • 3. 程序计数器
      • 虚拟机栈、本地方法栈、程序计数器可以线程共享吗
    • 三、线程共享
      • 1. 堆
      • 2. 方法区
      • 为什么堆和方法区是线程共享
    • 四、总结表

你有没有好奇过,Java 代码运行时,new出来的对象存在哪?方法执行时,方法本身存在哪?方法里的参数、局部变量又存在哪?一个类的元数据信息 —— 比如类的接口、父类、属性、函数,还有代码里的分支、循环、函数调用跳来跳去的,JVM 怎么知道代码执行到哪一行了?这些东西都是怎么存储的?要搞懂这些,就得先了解 JVM 内存结构。

一、JVM内存的核心划分

JVM 内存结构主要划分为这几个核心区域:虚拟机栈、本地方法栈、程序计数器、堆和方法区。
请添加图片描述

二、线程私有

线程私有区域指的是每个线程单独拥有一份,互不干扰,随线程创建而生成,随线程销毁而释放,不需要垃圾回收(GC)清理。

1. 虚拟机栈

虚拟机栈本质是一个栈结构(后进先出,LIFO),核心作用是管理方法的执行流程。我们调用任何一个方法时,JVM都会做一件事:创建一个「栈帧」(Stack Frame),并把它压入虚拟机栈;当方法执行完成(正常返回或抛出异常),这个栈帧就会被弹出栈。

比如执行 main() 方法时,先创建 main 栈帧压入栈;main 里调用 add() 方法,再创建 add 栈帧压入栈(此时 add 在栈顶);add 执行完弹出,回到 main 栈帧,直到 main 执行完弹出,程序结束——这就是方法执行的“后进先出”逻辑。

那方法里的局部变量、入参、出参这些东西存在哪?刚才说了,方法调用会产生栈帧,栈帧里的局部变量表就是用来保存局部变量和方法参数的;栈帧里还有操作数栈,专门用来做算术运算和方法调用的参数传递。栈帧里还有其他信息,但新手知道这两个核心部分就够了。
在这里插入图片描述

虚拟机栈需要 GC 吗?不需要!因为方法执行完就自动出栈,栈帧占用的内存也会跟着释放,全程自动管理,不用额外靠 GC 回收。

2. 本地方法栈

本地方法栈也是一个栈结构,所谓“本地方法”,就是用C/C++编写的方法(Java底层很多功能,比如IO操作、线程管理,都是调用本地方法实现的)。当Java代码调用 System.currentTimeMillis() 这种本地方法时,JVM会创建本地方法对应的栈帧,压入本地方法栈;方法执行完弹出,流程和虚拟机栈一样。

3. 程序计数器

程序计数器是JVM里最小的内存区域,核心作用是记录当前线程执行的字节码指令位置

因为代码里有分支、循环、方法调用,执行时会从这个类跳到另一个类,从这一行跳到那一行,所以需要程序计数器来保存 “即将执行的字节码指令的位置”。

  • 执行普通指令时,计数器记录“下一条要执行的字节码指令地址”;
  • 调用方法时,计数器记录“当前方法的返回地址”;
  • 多线程切换时,计数器会保存当前线程的执行位置——切换回该线程时,就能从上次的位置继续执行,不会乱序。

保存了位置之后,JVM 就知道下一步该执行哪行代码了。

虚拟机栈、本地方法栈、程序计数器可以线程共享吗

虚拟机栈可以线程共享吗?不行:不同线程可以并发执行,比如 A 线程调用 A 方法,B 线程调用 B 方法,肯定需要不同的虚拟机栈来保存各自正在执行的方法信息;本地方法栈也是同理。
而且不同线程执行的位置肯定不一样,执行到哪一行的进度也不同,所以必须每个线程单独用一个程序计数器记录自己的执行位置 —— 这些区域必须是线程私有的,各线程互不干扰。

三、线程共享

线程共享区域是JVM内存的“大头”,堆和方法区由所有线程共用,也是垃圾回收的主要战场(方法区在JDK1.8后有特殊处理)。

1. 堆

堆是线程共享的核心区域,是JVM中最大的内存区域,作用很明确:几乎所有new出来的对象都会存放在堆里。

那基本类型(比如int、char)放在哪?

  • 方法内的基本类型变量(比如 int age = 20):存在虚拟机栈的「局部变量表」里;
  • 基本类型数组(比如 int[] arr = new int[3]):数组对象本身存在堆里,数组里的元素(arr[0])也存在堆里;
  • 对象的基本类型属性(比如 User 类的 private int age):随对象一起存在堆里。

为什么基本类型放栈、对象放堆?不能都放一起吗?

这是 JVM 基于性能和使用场景的设计:

  1. 大小差异:基本类型(比如int占4字节)体积小,放栈里分配和销毁速度快;对象(比如一个复杂的 Order 对象)体积可能很大,放栈里会导致栈内存溢出;
  2. 生命周期差异:基本类型随方法执行而存在,方法结束就销毁;对象可能需要跨方法、跨线程存在(比如一个 User 对象被多个线程调用),堆的生命周期更长,更适合存储;
  3. 共享需求:对象需要被多个线程共享,堆是共享区域,只需通过“引用”(栈里的变量指向堆里的对象)就能访问;如果对象放栈里(线程私有),共享时需要深拷贝,效率极低。

所有对象都会放到堆里吗?

其实不一定。有些情况下,JVM 会对对象进行 “栈上分配”:通过 “逃逸分析” 判断对象是否只在当前方法内部使用 (没有作为返回值返回、没有传给其他方法或外部对象),这种 “没有逃逸” 的对象,JVM 会把它拆成基本类型,直接在虚拟机栈上分配,不用占用堆内存。

比如下面的代码,User 对象只在 createUser() 里使用,没有逃逸,就可能被栈上分配:

public void createUser() {User user = new User(); // 没有返回,没有传给其他方法user.setName("张三");
}

2. 方法区

方法区里放的不是方法,而是类的元数据信息

  • 类的基本信息:全类名(比如 com.test.User)、父类、实现的接口、访问修饰符;
  • 类的结构信息:字段(属性)的名称、类型、访问修饰符;方法的名称、参数列表、返回值类型、方法体字节码;
  • 静态变量:被 static 修饰的变量(比如 public static String name = "Java");
  • 常量池:存放字符串常量(比如 "hello")、符号引用(比如类名、方法名的引用)等。

方法区不同 JVM 的实现不一样:JDK1.8 之前,方法区的实现叫 “永久代”,是在JVM内存里面的,属于运行时数据区的一部分;JDK1.8 及之后,实现改成了 “元空间”,放到了本地内存(也就是操作系统的内存)里。

在这里插入图片描述

为什么要把永久代换成元空间,还放到本地内存?

永久代在JVM内存里,默认空间不大,当加载的类太多(比如SpringBoot项目依赖众多jar包),很容易触发 PermGen Space OOM

而元空间依赖操作系统内存,加载多少类的元数据信息就由系统的实际可用空间来控制,能加载更多类,大幅降低 OOM 风险。

为什么堆和方法区是线程共享

堆和方法区都是线程共享的,原因也很简单:堆里的对象需要被不同线程访问;方法区的类元数据是创建对象的 “模板”,多个线程创建同一类的对象时,都要读取这里的元数据,所以必须共享。

四、总结表

区域线程共享性核心存储内容生命周期是否需要GC
虚拟机栈私有栈帧(局部变量表、操作数栈等)随线程生灭不需要
本地方法栈私有本地方法的栈帧随线程生灭不需要
程序计数器私有当前线程执行的字节码指令位置随线程生灭不需要
共享new 出来的对象、数组随对象生灭(可跨线程)需要
方法区(元空间)共享类元数据、静态变量、常量池随类加载/卸载生灭部分需要
http://www.dtcms.com/a/589225.html

相关文章:

  • 【Vue 功能总结】Vue 注册功能实现:从校验到 API 封装
  • 站长工具收录查询网站建设培训 店
  • 建设家具网站的目的及功能定位做网站月入7000
  • 链表算法---基本算法操作(go语言版)
  • 【开题答辩全过程】以 基于SpringBoot房源出租信息系统的设计与实现为例,包含答辩的问题和答案
  • 解锁MIME:Qt中的数据传输密码
  • 【Go 与云原生】让一个 Go 项目脱离原生的操作系统——我们开始使用 Docker 制造云容器进行时
  • 大语言模型学习之路(一)
  • 网页设计教程网页设计培训福州关键词优化平台
  • GPIO中断编程
  • 手机和pc合一的网站云南集优科技网站
  • 14.大语言模型微调语料构建
  • Docker 40个自动化管理脚本
  • 国外html5网站模版网站建设代码流程
  • 基于多智能体技术的码头车辆最快行驶路径方案重构
  • 网站备案空壳制作微信网站
  • Java119 反射使用
  • 基于springboot付费问答系统【带源码和文档】
  • 焦作官网网站推广工具如何制作一个报名微信小程序
  • Babylon.js中PBRMetallicRoughnessMaterial材质系统深度解析:从基础到工程实践
  • Linuxgit入门设计模式(常考点)
  • Arbess CICD实践(3) - 使用Arbess+GitPuk+sourcefare实现Node.js项目自动化部署+代码扫描
  • 设计网站的流程网站如何做地推
  • 力扣154. 寻找旋转排序数组中的最小值 II
  • React构建工具升级
  • @Transactional注解的切点匹配
  • 建设网站 请示 报告淘宝网站制作公司哪家好
  • leetcode1770.执行乘法运算的最大分数
  • 本溪市城乡住房建设厅网站国外做网站侵权
  • 虚拟化入门笔记