JVM篇1:java的内存结构 + 对象分配理解

作为 Java 开发者,我们每天都在写 new Object(),但你是否想过,这行简单的代码背后,JVM 到底做了什么?它是如何管理内存的?对象到底被分配到了哪里?

本文将基于 JVM 的运行机制,深入剖析运行时数据区(Runtime Data Area)的内部结构,并还原一个对象从创建到分配内存的完整链路。

一、 什么是 JVM?

编译 + 管理, 简单来说,jvm的作用就是完成对代码的处理,让它运行, 并对一些垃圾进行回收。java之所以可以跨平台也多亏了jvm。

JVM(Java Virtual Machine)是 Java 程序运行的核心环境。它的主要职责可以概括为两点:"翻译官""大管家"

  • 翻译官:它屏蔽了底层操作系统的差异,让你写的 Java 代码可以"一次编写,到处运行"。

  • 大管家:它负责内存的自动分配和垃圾回收(GC),把程序员从繁琐的内存管理中解放出来。

JVM 的宏观运行流程

  1. 编译 :Java 源代码(.java)被编译器编译成字节码文件(.class)。

  2. 加载类加载系统 将字节码装载到运行时数据区

  3. 运行执行引擎 负责将字节码翻译为底层系统指令交由 CPU 执行。


二、 内存结构:运行时数据区

我们常说的 JVM 内存结构,指的就是 运行时数据区 。根据线程私有线程共享,可以将其划分为两大类。

1. 线程私有区域(图上蓝色区域)

这部分内存随着线程的创建而创建,随着线程的结束而销毁。这也是垃圾回收(GC)不需要关注的区域。

  • 程序计数器 (Program Counter Register)

    • 作用记住下一条jvm指令的执行地址!从而保证程序执行的有序性!

    • 本质:物理上通常通过 CPU 的寄存器实现。

    • 特点:它是 JVM 中唯一不会发生内存溢出(OOM)的区域。

  • 虚拟机栈 (VM Stack)

    • 别名 :线程栈。

    • 核心组成栈帧 (Stack Frame)。每个方法被调用时,都会创建一个栈帧,压入栈中;方法执行完毕,栈帧出栈。

    • 栈帧存储内容:方法参数、局部变量表、操作数栈、方法返回地址。

    • 异常 :如果方法递归调用过深(如死循环),会导致 java.lang.StackOverflowError

  • 本地方法栈 (Native Method Stack)

    • 作用 :为 native 方法服务。

    • 背景:Java 有时无法直接操作底层硬件或系统资源,需要通过 JNI(Java Native Interface)调用 C 或 C++ 编写的本地方法。

2. 线程共享区域(图上粉色区域)

这部分是所有线程都能访问的区域,也是 垃圾回收(GC)的主战场

  • 堆 (Heap)

  • 作用 :保存对象实例 。它是 JVM 管理的最大一块内存区域。

    • 逻辑划分

      • 年轻代 (Young Generation):存放生命周期短的对象。

      • 老年代 (Old Generation):存放生命周期长的对象。

    • 演进

      • JDK 1.7:堆中还包含永久代(方法区的实现)。

      • JDK 1.8:永久代移除,方法区移至本地内存。

  • 方法区 (Method Area)(可以理解为接口,永久代和元空间可以理解为他的实现类)

    • 作用:存储类信息、静态变量、常量、编译后的代码、运行时常量池。

    • 版本差异

      • JDK 1.7 及之前 :实现叫 永久代 (PermGen),占用堆内存,大小固定,容易 OOM。

      • JDK 1.8 及之后 :实现叫 元空间 (Metaspace) ,占用本地内存,大小可自动调整,不再受限于 JVM 堆大小。

3. 特殊区域:直接内存 (Direct Memory)

  • 定义:不属于 JVM 运行时数据区,而是操作系统的本机内存。

  • 场景:常见于 NIO 操作。利用 DirectBuffer 避免了数据在 Java 堆和 Native 堆之间来回复制(零拷贝),读写性能高,但分配成本也高。


三、 堆内存的精细化模型:Eden、Survivor 与 Region

为了更好地理解对象分配,我们需要把"堆"这一块拆解得更细致。

1. 经典分代模型

在传统的垃圾收集器(如 Serial, ParNew, CMS)视角下,堆被严格划分为:

  • 年轻代 (Young Gen)

    • Eden 区:绝大多数新对象诞生的地方(伊甸园)。

    • Survivor 区 (S0/S1):幸存者区。GC 后没死的对象在这里轮转。

  • 老年代 (Old Gen):存放长期存活的大对象。

2. G1 收集器的 Region 模型

随着内存越来越大,传统的整块划分导致回收停顿时间过长。G1(Garbage First)收集器引入了 Region 的概念:

  • Region:堆被划分为很多个大小相等的独立区域(Region)。

  • 每个 Region 逻辑上依然属于 Eden、Survivor 或 Old,但物理上它们不再是连续的。这让 JVM 可以更灵活地控制回收哪一部分内存。


四、 核心解密:对象的创建与分配流程

当我们执行 new User() 时,对象到底去了哪里?JVM 为了性能,设计了一套严密的**"三级分配策略"**。

举个例子:

java 复制代码
User user = new User();

这里user在栈上, 而new的 User在堆上, 可以简单理解为user就是一个栈上的指针操控着堆上真实的对象User();

1. 第一级:逃逸分析与栈上分配 (Stack Allocation)

JVM 首先通过逃逸分析技术,判断这个对象的作用域:

  • 未逃逸:对象只在当前方法内部使用,不会作为返回值给别人,也不会被其他线程访问。

  • 逃逸:对象可能被其他方法或线程引用(如:方法逃逸、线程逃逸)。

策略 : 如果对象未逃逸 ,JVM 可能通过优化,将对象直接分配在栈帧中。

  • 优势 :对象随方法结束自动销毁,不需要垃圾回收,极大地减轻了 GC 压力。

2. 第二级:TLAB 分配 (Thread Local Allocation Buffer)

因为多线程下创建对象为了防止冲突需要加锁,所以还不如直接先划分好一个一个的tlab给对象一个专属区域。如果tlab满了就只能eden公共区域去分配,这样的话就需要加锁了。

如果对象必须在堆中分配(发生逃逸或对象过大),JVM 会尝试走"VIP 通道"------TLAB。

  • 定义 :在堆的 Eden 区 中,为每个线程预先划分的一小块专属区域

  • 背景:多线程同时在堆中创建对象,为了防止内存冲突,必须加锁,导致性能下降。

  • 策略

    • 如果本线程的 TLAB 还有空间,直接在自己的 TLAB 里分配。

    • 优势无锁分配,效率极高。

    • 注意:TLAB 仅在"分配内存"这一刻是线程私有的,分配完成后,对象依然是线程共享的。

3. 第三级:公共堆分配 (Common Heap Allocation)

如果 TLAB 满了,或者对象太大 TLAB 装不下:

  • 策略 :对象只能在 Eden 区的公共区域 进行分配。

  • 代价 :需要加锁,与其他线程竞争内存空间。

总结:对象分配流程图

代码段

复制代码
graph TD
    A[new 对象] --> B{逃逸分析: 是否逃逸?}
    B -- 否 --> C[栈上分配]
    C --> D[方法结束, 自动销毁]
    B -- 是 --> E{TLAB是否够用?}
    E -- 是 --> F[TLAB分配 (Eden区专属块)]
    E -- 否 --> G[Eden区公共堆分配 (加锁)]
    F --> H[堆内存 (GC管理)]
    G --> H

结语

JVM 的内存管理不仅仅是简单的"堆和栈"。从 JDK 1.8 元空间的引入,到逃逸分析和 TLAB 的优化,再到 G1 收集器 Region 的设计,所有的演进都是为了在高并发 下实现更快的内存分配更高效的垃圾回收

理解这些,你就不仅仅是在写 Java 代码,而是在掌控代码的运行。

相关推荐
XXOOXRT2 小时前
基于SpringBoot的用户登录
java·spring boot·后端
萧曵 丶2 小时前
JVM Class中常量池 17 种 cp_info 表类型 浅谈
jvm·常量池
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 社团管理系统为例,包含答辩的问题和答案
java
努力也学不会java2 小时前
【Spring Cloud】环境和工程基本搭建
java·人工智能·后端·spring·spring cloud·容器
PuppyCoding2 小时前
EasyExcel 导出排除基类字段,不给基类加@ExcelIgnore 的方式。
java·开发语言
洲星河ZXH2 小时前
Java,泛型
java·开发语言·windows
海南java第二人2 小时前
SpringBoot循环依赖全解:从根源到解决方案的深度剖析
java·spring
CopyProfessor2 小时前
Java Agent 入门项目模板(含代码 + 配置 + 说明)
java·开发语言
duansamve2 小时前
VSCode中如何搭建JAVA+MAVEN开发环境?
java·vscode·maven