快速了解JVM中的深堆与浅堆

在Java虚拟机(JVM)的内存管理世界里,深堆与浅堆是两个重要的概念。它们如同衡量对象内存占用的两把标尺,对于优化程序性能、排查内存泄漏问题起着关键作用。接下来,让我们快速且深入地了解它们。

一、浅堆(Shallow Heap):对象的“基础重量”

浅堆指的是对象在内存中直接占用的空间,就像是一个人身上穿着的基础衣物,不包含他携带的其他物品。它的构成主要有以下几个部分:

  1. 对象头(Object Header):这部分存储着对象的关键信息,比如标记字(Mark Word),其中记录着哈希码、锁状态等内容,通常占用8字节;还有类型指针(Klass Pointer),用于指向对象的类元数据,在64位JVM中一般占8字节 。
  2. 实例数据(Instance Data):包含了对象中的各种字段。基本类型字段(如intlong)会按照其实际大小存储,例如int占4字节;而引用类型字段,则存储着指向其他对象的引用,通常占8字节。
  3. 对齐填充(Padding):JVM要求对象大小必须是8字节的整数倍,如果对象实际占用空间不足这个倍数,就会进行填充。
    浅堆结构图

通过下面的代码示例,我们可以更直观地感受浅堆的计算:

public class User {private String name;      // 引用类型:8字节private int age;          // 基本类型:4字节private List<Order> orders; // 引用类型:8字节
}

在这个User类中,假设对象头占用16字节,那么User对象的浅堆计算如下:8(name) + 4(age) + 8(orders) + 16(对象头) = 36字节,经过对齐填充后,最终可能是40字节(8的整数倍) 。

浅堆有两个关键特性:一是它的大小由对象的类结构决定,一旦对象被创建,其浅堆大小就固定不变;二是它不包含对象所引用的其他对象的内存,比如User对象的浅堆中,并不包含orders所指向的List对象的内存。

二、深堆(Retained Heap):对象的“影响力范围”

深堆表示的是当一个对象被垃圾回收(GC)后,实际能够释放的所有内存总和。它不仅包含对象自身的浅堆,还涵盖了该对象直接或间接引用的所有对象的内存,就好比一个人不仅自身占用空间,他携带的所有物品也会占用额外空间,这些物品就是他“影响力范围”内的内存。

计算深堆时,需要递归遍历对象的引用链:

  1. 首先是对象自身的浅堆。
  2. 然后是所有被该对象强引用的对象的浅堆。
  3. 接着是这些被引用对象再引用的其他对象的浅堆,以此类推,直到遍历完所有的强引用链。

同时,还要排除共享引用的情况。如果多个对象引用同一个对象(例如A和B都引用C),那么C的浅堆仅在计算首个引用对象(假设是A)的深堆时被计入,不会在计算B的深堆时重复计算。
深堆引用举例

看下面这个代码示例:

public class Order {private String orderId;   // 浅堆约24字节private List<Item> items; // 引用列表public Order(String orderId, List<Item> items) {this.orderId = orderId;this.items = items;}
}
// 创建订单及其商品列表
List<Item> items = new ArrayList<>();
for (int i = 0; i < 100; i++) {items.add(new Item("item" + i));
}
Order order = new Order("ORD123", items);

在这个例子中,order对象的深堆计算为:约24(Order自身) + 100 × 24(Item对象) = 2424字节 。

深堆有两个重要特性:一是它是动态变化的,会随着对象引用关系的改变而改变;二是如果一个对象的深堆为0,意味着它不可达,即从GC Roots(一组被JVM直接引用的对象,如栈变量、静态变量、JNI引用等)出发,无法通过任何强引用链访问到该对象,这样的对象是会被GC回收的。

三、深堆与浅堆的对比

为了更清晰地看出深堆与浅堆的差异,我们通过表格来进行对比:

维度浅堆(Shallow Heap)深堆(Retained Heap)
计算范围对象自身占用的内存对象及其强引用链覆盖的所有对象的内存
内存分析工具直接显示(如Heap Dump中的对象大小)通过工具计算(如MAT的"Retained Size")
典型应用分析单个对象的内存 footprint定位内存泄漏(如大对象的引用链)
GC回收条件无关(即使浅堆很大,若被引用则不会回收)深堆为0的对象才会被回收

四、为什么深堆为0的对象会被回收?

这是一个容易让人困惑的点,关键在于理解“不可达”的概念。深堆为0的对象,其自身浅堆确实存在,但由于它不可达,从GC Roots无法访问到它,因此它的回收不会释放任何额外内存(因为它不持有其他对象的强引用,或被引用的对象仍被其他GC Root引用) 。

通过以下代码演示:

public class GCDemo {public static void main(String[] args) {// 1. 创建对象A和B,A引用BA a = new A();B b = new B();a.b = b; // A的深堆 = A的浅堆 + B的浅堆// 2. 切断GC Root到A的引用a = null; // 变量a不再指向A实例,A实例变为不可达,深堆为0// 3. 此时虽然A实例的b字段仍指向B实例,但A不可达// 若要使B也不可达,需切断所有指向B的引用b = null; // 切断变量b对B实例的引用// 4. GC执行时,A和B都会被回收System.gc();}
}class A {B b; // 引用B
}class B {int value;
}

在步骤2之前,A的深堆 = A的浅堆(24字节) + B的浅堆(16字节) = 40字节,此时B可达,因为被A引用且被变量b引用;而在步骤2之后,a = null使得A不可达(深堆为0),但此时B仍然可通过变量b访问 。只有在执行b = null后,B才变为不可达。最终,A和B的浅堆都被释放,在A不可达时,其深堆为0(不包含自身浅堆)。

这里需要特别注意:Java中变量引用对象内部引用是不同的概念。当执行a = null时,只是切断了变量a对A实例的引用,而A实例内部的b字段对B实例的引用在A实例被回收前依然存在

栈内存                  堆内存
a                       A实例↓b字段 ───────────→ B实例
b ──────────────────→ B实例

只有当所有指向对象的引用都被切断(包括变量引用和对象内部引用),对象才会真正变为不可达,进而被GC回收。

五、实战案例:通过MAT分析内存泄漏

在实际项目中,我们可能会遇到系统频繁Full GC,堆内存却居高不下的情况。这时,我们可以通过生成Heap Dump文件,并使用内存分析工具(如MAT,Memory Analyzer Tool)来分析深堆与浅堆,找出内存泄漏的原因。

例如,我们发现一个byte[]数组,它的浅堆很大,达到了100MB,存储着临时文件内容。但如果它没有被长生命周期对象引用,GC会及时回收它,所以它不一定是内存泄漏的根源。

而当我们发现一个静态Map,它缓存了大量User对象,每个User对象又关联多个Order对象时,由于静态Map是GC Root,它引用的所有对象深堆均不为0,这就很可能导致内存泄漏。

针对这种情况,我们可以使用弱引用(WeakReference)来避免内存泄漏:

// 使用弱引用避免内存泄漏
private static final Map<Key, WeakReference<Value>> cache = new WeakHashMap<>();// 显式清理过期缓存
public void removeOldEntries() {cache.entrySet().removeIf(entry -> entry.getValue().get() == null);
}

六、常见误区与最佳实践

在理解深堆与浅堆的过程中,存在一些常见的误区:

  1. 误区1:“频繁创建小对象不会导致内存问题”。事实是,如果这些小对象被静态集合引用,深堆会持续增长,最终可能导致内存溢出(OOM)。
  2. 误区2:“调用System.gc()能立即回收所有无用对象”。实际上,System.gc()只是建议GC执行,实际回收时机由JVM决定,而且GC只会回收深堆为0的对象。

为了更好地管理内存,我们可以遵循以下最佳实践:

  1. 优先关注深堆:使用MAT等工具分析对象的Retained Heap,找出真正占用大量内存的对象及其引用链。
  2. 控制引用链长度:避免长生命周期对象(如单例)持有短生命周期对象的强引用。
  3. 使用合适的引用类型:例如,在缓存大对象时使用软引用(SoftReference),这样在内存不足时对象会自动被回收。
// 缓存大对象时使用软引用,内存不足时自动回收
private static final Map<Key, SoftReference<LargeObject>> cache = new HashMap<>();

七、总结

深堆与浅堆是JVM内存管理中不可或缺的概念。浅堆反映了对象自身的“基础重量”,体现了对象的类结构设计;而深堆则展示了对象的“影响力范围”,决定了对象是否能被GC回收。

在实际的开发和调优过程中,当遇到内存问题时,我们可以按照以下步骤进行排查:首先使用jmapjcmd生成Heap Dump文件;然后利用MAT分析深堆大的对象及其引用链;最后检查GC Roots,找出不必要的强引用关系。掌握深堆与浅堆的知识,将为我们优化Java程序的内存使用提供有力的支持。

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

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

相关文章

开疆智能ModbusTCP转Devicenet网关连接FANUC机器人配置案例

本案例是ModbusTCP主站通过开疆智能ModbusTCP转Devicenet网关连接发那科机器人的配置案例&#xff0c;操作分为三个配置1&#xff1a;ModbusTCP主站配置2&#xff1a;ModbusTCP转Devicenet网关配置3&#xff1a;FANUC机器人配置&#xff0c;具体过程如下 配置过程 主菜单—IO—…

详解RabbitMQ高级特性之发送方确认机制

目录 发送方确认 添加配置 常量类 声明队列和交换机并绑定二者关系 confirm确认模式 编写生产消息代码 生产消息1 解决方法 多次生产消息2 解决方法 生产消息3 return 模式 编写生产消息代码&#xff08;路由正确&#xff09; 生产消息1 编写生产消息代码&…

Google Play开发者账号8.3/10.3政策违规自救指南

最近&#xff0c;有一位开发者焦急地向我们诉说&#xff0c;其辛苦开发的多个应用&#xff0c;毫无征兆地全部下架&#xff0c;账户提示违反政策 8.3 和 10.3。经过连夜排查&#xff0c;原来是换皮应用与误导性描述导致的问题。 这并非个例&#xff0c;在 2024 年&#xff0c;G…

pythonday50

作业&#xff1a; 1.好好理解下resnet18的模型结构 2.尝试对vgg16cbam进行微调策略 import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms from torchvision import models from torch.utils.d…

天猫618高增长背后:电商迈入价值战新周期

作者 | 曾响铃 文 | 响铃说 这次618&#xff0c;来“真”的了。 天猫618玩法变得极致简单&#xff0c;只设了“官方立减”的85折的基础优惠&#xff0c;再叠加行业品类券、国补等优惠&#xff0c;最高立减可达50%&#xff0c;十分直观。 让消费者省心的结果也是显而易见的&…

tauri+vue自动更新客户端打包配置

拉取最新代码打开项目根目录下"~.tauri\myapp.key"文件并复制内容 打开项目的powershell窗口&#xff0c;输入如下内容并回车 $env:TAURI_SIGNING_PRIVATE_KEY"复制的myapp.key" $env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD""然后修改tauri.conf.…

硬件------51单片机

一.基本概念 1.裸机程序 BSP BSP&#xff1a;bord suppord pack 板级支持包 就是程序编写的内容是没有操作系统的&#xff0c;直接通过代码去控制寄存器&#xff0c;让硬件按照要求去工作。 主要内容&#xff1a;51单片机 IMAX6ULL 2.linux驱动部分 在裸机BSP程序的基础…

java 基础方法 list分页

新增一个list 泛型分类方法 hutools没这个方法, mybatis 里面的方法不好用 故新增此方法 package com.common.base.util.page;import lombok.Data;import java.util.List;/*** className: VoPage* description: list分页* author: chenyuanlong* date: 2025年6月16日 0016 上午…

操作系统期末复习--操作系统初识以及进程与线程

操作系统概念与主要功能 操作系统的概念 在信息化时代&#xff0c;软件是计算机系统的灵魂&#xff0c;而作为软件核心的操作系统&#xff0c;已与现代计算机系统密不可分、融为一体。计算机系统自下而上大致分为4部分&#xff1a;硬件、操作系统、应用程序和用户 操作系统管…

使用jhat查看dump.hprof文件内具体对象的属性值信息

jhat是JDK自带的堆转储分析工具&#xff0c;可以用来查看.hprof文件中对象的具体内容。本文演示使用的是JKD8. 一、启动jhat 执行启动命令。 jhat -J-Xmx4g your_heap_dump.hprof -J-Xmx4g表示为jhat分配4GB内存&#xff0c;根据你自己情况调整大小。your_heap_dump.hprof是…

freeRTOS之队列(queue)

一.概述 1.介绍 队列(queue)可以用于"任务到任务"、“任务到中断”、"中断到任务"直接传输信息。 2.核心功能 线程安全&#xff1a;自动处理多任务访问时的互斥问题。 数据复制&#xff1a;入队时复制数据&#xff08;而非引用&#xff09;&#xff0c;…

【python】typing用法

一、基础类型提示 1. 基本类型注解 # 变量类型注解 age: int 30 name: str "Alice" is_student: bool False height: float 1.752. 函数注解 def greet(name: str, age: int) -> str:return f"Hello {name}, you are {age} years old!"二、组合类…

web前端开发核心基础:Html结构分析,head,body,不同标签的作用

前端技术协同关系 协作流程&#xff1a;HTML构建页面框架—>css美化样式&#xff08;选择器属性&#xff09;—>JavaScript实现交互&#xff08;类似于python的脚本语言&#xff09;扩展基础&#xff1a;在上面三项基础上学习Vue\React、构建工具WePack和浏览器工作原理…

精益数据分析(105/126):移动应用核心指标解析与用户分层营收策略

精益数据分析&#xff08;105/126&#xff09;&#xff1a;移动应用核心指标解析与用户分层营收策略 在移动应用市场竞争白热化的今天&#xff0c;单纯追求下载量已无法保证商业成功&#xff0c;精细化运营核心指标成为盈利关键。本文将深入解析每日活跃用户平均营收&#xff…

被CC攻击了,对服务器有什么影响?

博客正文&#xff1a; 最近&#xff0c;不少网站管理员和运维人员反映遭遇了CC攻击&#xff0c;导致服务器性能异常甚至瘫痪。那么&#xff0c;CC攻击究竟会对服务器造成哪些影响&#xff1f;本文将为你简要解析CC攻击的原理及其带来的危害&#xff0c;帮助你更好地理解并应对…

Tensorflow安装出现dependency conflict错误

Python版本&#xff1a; 3.11.4 pip版本已升到最新 电脑上有mac的原装Python2.x&#xff0c;我装的3.11.4&#xff0c;还有个什么依赖的3.9 运行 pip3 install tensorflow 出现类似以下错误 &#xff08;我报错的是另一个不是tensorflow—estimator&#xff0c;但基本就是…

2025年HTTP半开与错误攻击防御指南:原理拆解与实战防护

你以为限流就能防住HTTP攻击&#xff1f;黑客用协议畸形包AI调度正在撕裂传统防线&#xff01; 一、HTTP半开攻击&#xff1a;慢速绞杀服务器资源 ▶ 攻击原理剖析 HTTP半开攻击&#xff08;如Slowloris&#xff09;是一种应用层DoS攻击&#xff0c;通过建立大量半开连接耗尽…

Mybatis(XML映射文件、动态SQL)

目录 基础操作 准备&#xff1a; 删除&#xff1a; 新增&#xff1a; 更新&#xff1a; 查询&#xff1a; 条件查询&#xff1a; XML映射文件 动态SQL if foreach sql&include 基础操作 准备&#xff1a; 准备数据库表 创建一个新的springboot工程&#xff0…

python校园拼团系统

目录 技术栈介绍具体实现截图系统设计研究方法&#xff1a;设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理&#xff0c;难度适中&#xf…

多模态大语言模型arxiv论文略读(127)

When SAM2 Meets Video Camouflaged Object Segmentation: A Comprehensive Evaluation and Adaptation ➡️ 论文标题&#xff1a;When SAM2 Meets Video Camouflaged Object Segmentation: A Comprehensive Evaluation and Adaptation ➡️ 论文作者&#xff1a;Yuli Zhou, …