第一章:引言

Java 是一种面向对象的编程语言,对象(Object)是其最基本的组成单位。Java 的“一切皆对象”不仅体现在语法层面,更体现在运行时,几乎所有数据都以对象形式存在于内存中。

然而,很多开发者对 Java 对象的理解还停留在语言层面,比如 new 关键字、类结构、方法调用等,却对底层 JVM 是如何创建、布局、管理这些对象知之甚少。

在性能调优、内存泄漏分析、高并发系统开发、或处理复杂对象图结构时,深入理解 Java 对象在 JVM 层面的行为就显得至关重要。

第二章:JVM 内存结构概览

要理解 Java 对象在 JVM 中的行为,首先要掌握 JVM 的整体内存结构。Java 虚拟机将运行时数据区划分为若干区域,每一部分都有特定的职责。

Java 内存区域全解

根据 Java 虚拟机规范,JVM 的主要内存结构如下:

1. 程序计数器(Program Counter Register)

  • 每条线程都有独立的程序计数器,是线程私有的内存空间。

  • 记录当前线程所执行的字节码指令地址。

  • 如果线程正在执行的是一个 native 方法,那么该计数器值为 undefined。

2. 虚拟机栈(JVM Stack)

  • 每个方法被调用时都会创建一个栈帧(Stack Frame)。

  • 包含局部变量表、操作数栈、动态链接、返回地址等。

  • 线程私有,随线程创建而创建,随线程销毁而销毁。

  • 抛出 java.lang.StackOverflowError 通常是由于栈帧过深或死递归导致。

3. 本地方法栈(Native Method Stack)

  • 为虚拟机使用到的 native 方法服务。

  • 类似于 JVM 栈,只不过用于本地方法。

  • 并不是所有 JVM 都实现这个栈,HotSpot 把 JVM 栈与本地方法栈合并实现。

4. Java 堆(Heap)

  • 所有对象实例和数组的内存都在这里分配。

  • 是垃圾收集器管理的主要区域,也被称作 GC 堆。

  • 在 JVM 启动时创建,整个 JVM 进程中只有一个。

  • 可通过 -Xms-Xmx 设置最小/最大堆大小。

Heap 的分代结构(HotSpot 实现)
  • 新生代(Young Generation)

    • 包括 Eden 和两个 Survivor 区域(S0 / S1)。

    • 新生对象一般先分配在 Eden 中。

  • 老年代(Old Generation)

    • 存活时间较长的对象会被晋升到老年代。

5. 方法区(Method Area)

  • 存储已被虚拟机加载的类信息、常量、静态变量、JIT 编译后的代码等。

  • 属于线程共享区域。

  • Java 8 之前叫做 Permanent Generation(永久代)。

  • Java 8 起使用本地内存中的 Metaspace 替代永久代。

Metaspace 特点:
  • 存储类元数据(类的结构定义,如字段、方法等)。

  • 分配在本地内存(非堆内存)中,大小受操作系统限制。

  • 参数调整示例:

-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m

6. 运行时常量池(Runtime Constant Pool)

  • 每个类或接口都有自己的常量池表。

  • 包括字面量(如字符串常量)和符号引用(如类、方法、字段的符号引用)。

  • 位于方法区(Java 8 中即 Metaspace)中。

7. 直接内存(Direct Memory)

  • 并非 JVM 运行时数据区的一部分。

  • java.nio 包中的 ByteBuffer.allocateDirect() 直接分配。

  • 属于操作系统层级的内存,绕过 JVM 堆,减少复制,提高性能。

  • 大量使用会导致 OutOfMemoryError: Direct buffer memory


通过掌握 JVM 的内存结构,我们可以更好地理解 Java 对象为何分配在某个区域,以及这些内存区域对对象生命周期和性能有怎样的影响。

第三章:Java 对象的创建过程

Java 对象的创建在 JVM 中并不是一句 new 指令那么简单,它涉及类加载机制、内存分配策略、并发安全控制、对象头初始化等多个底层细节。

1. 创建流程概览

  1. 类加载检查

  2. 分配内存

  3. 初始化零值

  4. 设置对象头

  5. 执行构造函数

2. 类加载检查

 Java 类加载机制详解

当 JVM 执行 new 指令时,首先检查该类是否已经被加载、解析与初始化。若未加载,会触发类加载过程,遵循双亲委派机制。

Class<?> clazz = Class.forName("com.example.Person");

只有类加载完成后,JVM 才允许创建其实例。

3. 内存分配

对象实例的内存一般分配在堆上。JVM 中使用以下几种策略进行分配:

3.1 指针碰撞(Bump-the-pointer)
  • 适用于堆内存连续的情况;

  • 分配时只需移动一个“空闲指针”;

  • 高效但对堆碎片要求高。

3.2 空闲列表(Free List)
  • 适用于堆内存不连续的情况;

  • 使用空闲内存块列表管理内存;

  • 分配成本高于指针碰撞。

3.3 TLAB(Thread Local Allocation Buffer)
  • Java 8 默认开启;

  • 为每个线程分配私有缓冲区,避免锁争用;

  • 启动参数:

    -XX:+UseTLAB -XX:+PrintTLAB

4. 默认值初始化

内存分配后,JVM 会将对象字段初始化为默认值:

public class Person {int age;         // 默认 0boolean active;  // 默认 falseString name;     // 默认 null
}

此阶段仅进行“零值填充”,尚未执行构造函数逻辑。

5. 设置对象头

每个 Java 对象都有一个对象头(Object Header),包含两部分:

  • Mark Word:存储哈希码、GC 年龄、锁标志位等;

  • 类型指针(Klass Pointer):指向类元数据(即 Class 对象)。

+------------------+--------------------------+
|     Mark Word    |   Klass Pointer(类指针) |
+------------------+--------------------------+

6. 执行构造函数

最后,JVM 会执行对象对应的构造函数(字节码中的 <init> 方法),完成字段赋值、逻辑初始化等操作:

Person p = new Person("Alice", 30);

这时对象才真正具备业务语义。


小结

Java 中一句简单的 new,在 JVM 内部需要经历:

  • 类是否已加载

  • 采用何种内存分配策略

  • 字段默认值填充

  • 设置对象头(用于 GC/锁等)

  • 执行构造逻辑

理解这一过程有助于我们更精准地定位对象创建带来的性能问题,如频繁 GC、大量临时对象分配等。

第四章:Java 对象的内存布局

Java 对象在 JVM 内存中的实际结构是由虚拟机内部定义的,通常包括以下三部分:

  1. 对象头(Object Header)

  2. 实例数据(Instance Data)

  3. 对齐填充(Padding)

4.1 对象头(Object Header)

对象头通常包含两部分:

  • Mark Word:存储对象的哈希码、GC 分代年龄、锁信息等。

  • Class Pointer:指向对象所属类的元数据(Klass 指针)。

在 64 位 JVM 中,还可能包括压缩类指针或对象指针,这取决于是否启用了如下 VM 参数:

-XX:+UseCompressedOops -XX:+UseCompressedClassPointers

这些压缩技术能有效降低指针所占空间,从而节省整体内存消耗。

4.2 实例数据(Instance Data)

实例数据部分存储类中声明的所有字段值,包括从父类继承的字段。字段的内存排列顺序通常按照以下规则优化:

  • 父类字段排在子类字段之前;

  • 同一类型的字段尽可能排列在一起,以提高缓存效率;

  • boolean 等小字段可能会被重排聚合,减少内存碎片。

4.3 对齐填充(Padding)

JVM 要求对象的总大小是 8 字节的倍数。如果对象头和实例数据加起来不是 8 的倍数,JVM 会在末尾填充字节来对齐。

这部分填充是内部机制,程序中不可见,但会增加对象内存总开销。


示例:使用 JOL 查看对象布局

我们可以使用 JOL(Java Object Layout)工具来直观查看 Java 对象的内存布局。

示例类:
public class Simple {int x;boolean flag;
}
使用 JOL 分析:
import org.openjdk.jol.info.ClassLayout;public class Main {public static void main(String[] args) {Simple simple = new Simple();System.out.println(ClassLayout.parseInstance(simple).toPrintable());}
}
输出结构示意:
OFFSET  SIZE    TYPE               DESCRIPTION
0       8       (object header)    Mark Word
8       4       int                Simple.x
12      1       boolean            Simple.flag
13      3       (alignment gap)    Padding to 16 bytes

注:最终内存布局取决于 JVM 设置和字段声明顺序。


小结

Java 对象的内存布局对理解 JVM 行为、优化性能、调试问题都至关重要。它直接影响如下方面:

  • GC 扫描与压缩行为

  • 锁机制实现(偏向锁、轻量级锁等)

  • 对象大小与内存占用估算

  • 字段访问性能优化

理解对象头、字段排列与对齐规则,是掌握 JVM 对象模型的关键一步。

第五章:对象的访问定位方式

在 Java 中,对象并非通过裸地址直接访问,而是依赖 JVM 内部的访问定位机制。主要有两种对象定位方式:

  1. 句柄访问(Handle Access)

  2. 直接指针访问(Direct Pointer Access)

不同的 JVM 实现可能采用不同的方式。以 HotSpot 为例,默认采用的是直接指针访问方式。


5.1 句柄访问方式

在句柄访问方式中,Java 堆中划出一块句柄池(Handle Pool),对象的引用变量实际上指向的是句柄,而不是对象本身。句柄中包含两个指针:

  • 指向对象实例数据的指针;

  • 指向对象类型元数据的指针。

示例结构:
引用变量↓句柄(Handle)↙         ↘
对象地址    类型元数据地址
优点:
  • 对象在 GC 移动时,只需更新句柄中的指针,引用不变;

  • 实现更加稳定、适用于移动频繁的对象。

缺点:
  • 每次访问需两次指针解引用,性能较低。


5.2 直接指针访问方式(HotSpot 默认)

在直接指针方式下,对象引用变量直接保存对象在堆中的地址。对象头中存储着类型信息。

示例结构:
引用变量↓
对象实例地址(含类型元数据指针)
优点:
  • 访问速度快,仅一次指针解引用;

  • 结构更紧凑。

缺点:
  • 如果对象在 GC 中被移动,必须更新所有指向它的引用。


5.3 对比总结

访问方式引用中存储内容优点缺点
句柄访问句柄地址对象移动时引用不变,结构稳定每次访问多一次间接寻址
直接指针访问对象地址性能高,访问快对象移动需更新所有引用

5.4 与压缩指针配合使用

Java 8 引入了指针压缩(Compressed OOPs)机制,在启用 64 位 JVM 的同时,允许引用仍使用 32 位地址存储,从而节省空间。

启用参数:
-XX:+UseCompressedOops
-XX:+UseCompressedClassPointers

通过这些参数,引用字段仍可仅占用 4 字节空间,提升了对象布局的紧凑性和内存利用率。


5.5 工具验证:JOL 观察引用偏移量

结合 JOL 工具可以观察引用类型字段的内存偏移,间接推断 JVM 是否启用了压缩指针。

public class RefTest {Object ref;
}public class Main {public static void main(String[] args) {System.out.println(ClassLayout.parseInstance(new RefTest()).toPrintable());}
}

若字段 ref 的偏移量为 12(非 16),说明使用了压缩引用。


小结

对象的访问定位方式影响着 JVM 的访问性能、GC 策略和内存使用:

  • HotSpot 采用 直接指针访问,配合压缩指针优化性能与空间;

  • 句柄方式 提供更高的内存迁移灵活性,适合对象频繁移动的环境;

  • 工具如 JOL 能协助我们理解 JVM 内部结构布局。

理解对象的访问方式是深入掌握 JVM 内部工作机制的重要一环,有助于我们在高性能系统中做出更合理的内存布局与 GC 策略决策。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88682.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88682.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/88682.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis 基本操作笔记

1. Redis 简介 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、高性能的键值对存储系统&#xff0c;通常作为数据库、缓存、消息中间件等使用。它支持多种数据类型&#xff0c;包括字符串、哈希、列表、集合、有序集合等。 Redis 特点&#xff1a; 性能&…

Docker从环境配置到应用上云的极简路径

Docker从环境配置到应用上云的极简路径主要包括环境配置、应用容器化、选择云平台及部署应用等步骤&#xff0c;具体如下&#xff1a; - 配置Docker环境&#xff1a; - 安装Docker&#xff1a;根据操作系统下载对应版本的Docker安装包。如在Linux系统中&#xff0c;可使用命令…

Slicer渲染Dicom到nrrd

Slicer渲染Dicom到nrrd 工作中遇到一些处理Dicom数据的需求&#xff0c;个人通过网络上的一些教程 对于原始数据尝试转换到nrrd时&#xff0c;发现部分的窗体数据的渲染方向不一致 进一步发现这些很多定义的方向是跟设备厂家强相关的&#xff0c;不同厂家对于同一段的Dicom参…

QT中设计qss字体样式但是没有用【已解决】

检查一下stylesheet里面是不是有不能被QT读取的CSS语言&#xff0c;可能会跟字体颜色冲突错误示范&#xff1a;/* 错误示例&#xff1a;QSS 中使用 box-shadow */ QPushButton {box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); /* Qt 不支持此属性 */ }删掉就行了如果后续想用阴影…

uniapp获取状态栏高度,胶囊按钮的高度,底部安全区域的高度,自定义导航栏

相关API uni.getSystemInfoSync() uni.getMenuButtonBoundingClientRect() 创建一个utils文件夹&#xff0c;该文件下封装一个systemInfo.js /*** 系统信息工具类* 封装获取系统状态栏、导航栏和安全区域等相关信息的方法*/// 获取系统信息并缓存 const systemInfo uni.get…

jQuery 文本属性值

一、前言在网页开发中&#xff0c;我们经常需要对页面上的文本内容进行操作&#xff0c;例如动态修改段落文字、读取用户输入框的内容、更新按钮文本等。jQuery 提供了简洁而强大的方法来处理这些常见的文本操作需求。本文将带你全面了解 jQuery 中用于操作文本内容的三个核心方…

JAVA并发——为什么Java中的ThreadLocal对key的引用为弱引用

1、ThreadLocal 的用途 给每个线程提供自己独立的变量副本&#xff0c;实现线程间隔离。 常用于&#xff1a; 数据库连接、Session 缓存、用户上下文&#xff08;如 userId&#xff09;线程池中的线程复用时避免共享污染&#xff1b;实现线程封闭的设计模式 2、内存泄漏 使用弱…

【C++】多线程同步三剑客介绍

目录 条件变量 头文件 主要操作函数 1、等待操作 2、唤醒操作 使用示例 信号量 头文件 主要操作函数 1、信号量初始化 2、等待操作&#xff08;P操作&#xff09; 3、信号操作&#xff08;V操作&#xff09; 4、获取信号量值 5、销毁信号量 使用示例 互斥锁 …

《Java Web程序设计》实验报告八 JSP+Servlet+JDBC+MySQL实现课程管理

目 录 一、实验目的 二、实验环境 三、实验步骤和内容 1、小组成员分工&#xff08;共计4人&#xff09; 2、实验方案 3、实验结果与分析 4、项目任务评价 四、遇到的问题和解决方法 五、实验总结 一、实验目的 1、掌握mysql的安装、数据库表单创建 2、掌握JDBC的链接…

基于数据挖掘的课程推荐系统研究

摘要本研究设计并开发了一套基于先进数据挖掘技术的智能化课程推荐系统。该系统创新性地采用了协同过滤算法与内容推荐算法相结合的混合推荐策略&#xff0c;通过深度分析学生在学习平台上的历史行为数据&#xff08;包括选课记录、学习时长、测试成绩等&#xff09;以及课程的…

【SCI 4区推荐】《Journal of Visual Communication and Image Representation》

期刊简介&#xff1a;《视觉传达与图像表示杂志》&#xff08;Journal of Visual Communication and Image Representation&#xff09;致力于发表视觉传达与图像表示领域的最前沿研究&#xff0c;特别强调多学科交叉领域中的新技术和理论应用。这本期刊涵盖的研究范围广泛&…

20250711_Sudo 靶机复盘

target:192.168.43.20 外部打点 &#xff08;文件上传&#xff09; nmap扫一下&#xff0c;80,22 开放 扫目录&#xff0c;发现 README.md [17:04:30] 200 - 664B - /Dockerfile [17:04:38] 200 - 34KB - /LICENSE …

STEP 7-Micro/WIN SMART 编程软件:从入门到精通的使用指南

STEP 7-Micro/WIN SMART 编程软件&#xff1a;从入门到精通的使用指南 在工业自动化控制领域&#xff0c;编程软件是连接工程师与 PLC 的桥梁&#xff0c;而 STEP 7-Micro/WIN SMART 作为 S7-200 SMART PLC 的专用编程工具&#xff0c;以其友好的界面和高效的编程能力备受青睐。…

模型训练与部署注意事项篇---resize

图像大小的影响在 YOLOv 系列模型的训练和推理部署过程中&#xff0c;图像大小的选择是影响模型性能&#xff08;精度、速度、泛化能力&#xff09;的关键因素之一。两者的关系既相互关联&#xff0c;又存在一定的灵活性&#xff0c;具体可从以下几个方面详细分析&#xff1a;一…

【Python】venv:配置独立镜像源

为某个特定的 venv 虚拟环境设置 pip 镜像源&#xff0c;使得该环境下的 pip 安装始终使用自定义镜像源&#xff0c;不影响系统 pip&#xff0c;也不依赖用户级配置文件。环境准备 1. 创建虚拟环境 python -m venv venv2. 激活虚拟环境Windows: .\venv\Scripts\activateLinux/m…

日本语言学校:签证制度类 Prompt 的结构整理路径与策略

日本语言学校&#xff1a;签证制度类 Prompt 的结构整理路径与策略 我们在构建语言留学语义系统的过程中&#xff0c;尝试以“签证风险”为例&#xff0c;探索如何让结构信息被更好地保留下来。本文不介绍 Prompt 本身&#xff0c;也不夸大其作用&#xff0c;而是希望借此与更…

RFCOMM协议详解:串口仿真与TCP/IP协议栈移植技术——面试高频考点与真题解析

一、RFCOMM 协议核心考点与高频面试问题1.1 协议基础与核心功能考点解析&#xff1a;RFCOMM&#xff08;Radio Frequency Communication&#xff09;是蓝牙协议栈中实现串口仿真的核心协议&#xff0c;基于 L2CAP 协议提供类似 RS-232 的可靠数据流传输。其核心功能包括&#x…

【编程实践】利用open3d生成物体的最长边方向并可视化

1 利用3d软件生成一个长方体 边长随意&#xff0c;长度随意 2 导出为模型文件并采样为点云数据 从mesh表面进行采样&#xff0c;点数根据自己需求进行设置&#xff0c;此处设置为100000。采样结果&#xff1a;3 识别OBB外接框并可视化长边方向import numpy as np import open3d…

1. 好的设计原则

目录一、应该具备的性质二、面向对象设计原则三、详解3.1 开闭原则3.2 单一职责原则3.3 里氏替换原则3.4 依赖倒置原则3.5 接口隔离原则3.6 合成复用原则3.7 迪米特原则一、应该具备的性质 可扩展性灵活性可插入性 二、面向对象设计原则 以下设计原则的重要性从高到低排列 …

深度学习图像分类数据集—猫七种表情识别分类

该数据集为图像分类数据集&#xff0c;适用于ResNet、VGG等卷积神经网络&#xff0c;SENet、CBAM等注意力机制相关算法&#xff0c;Vision Transformer等Transformer相关算法。 数据集信息介绍&#xff1a;猫七种表情识别分类&#xff1a;[Angry, Disgusted, Happy, Normal, Sa…