快速了解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
英文地址,請注明出處:http://en.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, …