JVM運行時數據區之堆空間

来源:https://www.cnblogs.com/dupengpeng/archive/2023/07/22/17573785.html
-Advertisement-
Play Games

# JVM運行時數據區之堆空間 ## 1.核心概述 一個JVM實例只存在一個堆記憶體,堆也是Java記憶體管理的核心區域。堆區在**JVM 啟動的時候即被創建**,其空間大小也就確定了,是**JVM管理的最大一塊記憶體空間**。 《Java虛擬機規範》中對Java堆的描述是:所有的對象實例以及數組都應當在 ...


JVM運行時數據區之堆空間

1.核心概述

一個JVM實例只存在一個堆記憶體,堆也是Java記憶體管理的核心區域。堆區在JVM 啟動的時候即被創建,其空間大小也就確定了,是JVM管理的最大一塊記憶體空間

《Java虛擬機規範》中對Java堆的描述是:所有的對象實例以及數組都應當在運行時分配在堆上。(The heap is the run-time data area fromwhich memory for all class instances and arrays is allocated)我要說的是:“幾乎”所有的對象實例都在這裡分配記憶體。一從實際使用角度看的。

  • 數組和對象可能永遠不會存儲在棧上,因為棧幀中保存引用,這個引用指向對象或者數組在堆中的位置。
  • 在方法結束後,堆中的對象不會馬上被移除,僅僅在垃圾收集的時候才會被移除。
  • 堆,是GC ( Garbage Collection,垃圾收集器)執行垃圾回收的重點區域。

2.內部結構

現代垃圾收集器大部分都基於分代收集理論設計,堆空間的內部結構細分為:

Java 7及之前堆記憶體邏輯上分為三部分:新生區+養老區+永久區

  • YoungGeneration Space 新生區 Young/New
    • 又被劃分為Eden區和Survivor區
  • Tenure generation space 養老區 Old/Tenure
  • Permanent Space 永久區 Perm

Java 8及之後堆記憶體邏輯上分為三部分: 新生區+養老區+元空間

  • YoungGeneration Space 新生區 Young/New
    • 又被劃分為Eden區和Survivor區
  • Tenure generation space 養老區 Old/Tenure
  • Meta Space 元空間 Meta

JDK7的內部結構圖

變化

3.年輕代與老年代

存儲在JVM中的Java對象可以被劃分為兩類:

  • 一類是生命周期較短的瞬時對象,這類對象的創建和消亡都非常迅速

  • 另外一類對象的生命周期卻非常長,在某些極端的情況下還能夠與JVM的生命周期保持一致。

Java堆區進一步細分的話,可以劃分為其中年輕代又可以劃分為Eden空間、Survivor0空間和Survivor1空間(有時也叫做from區、to區)。

3.1配置

下麵這參數開發中一般不會調:
配置新生代與老年代在堆結構的占比

  • 預設-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整個堆的1/3

  • 可以修改-XX:NewRatio=4,表示新生代占1,老年代占4,新生代占整個堆的1/5

在HotSpot中,Eden空間和另外兩個Survivor空間預設所占的比例是8:1:1,當然開發人員可以通過選項 -XX:SurvivorRatio”調整這個空間比例。比如-XX:SurvivorRatio=8

幾乎所有的Java對象都是在Eden區被new出來的。絕大部分的Java對象的銷毀都在新生代進行了。IBM公司的專門研究表明,新生代中 80%的對象都是“朝生夕死”的。

4.對象分配過程

  1. 為新對象分配記憶體是一件非常嚴謹和複雜的任務,JVM的設計者們不僅需要考慮記憶體如何分配、在哪裡分配等問題,並且由於記憶體分配演算法與記憶體回收演算法密切相關,所以還需要考慮GC執行完記憶體回收後是否會在記憶體空間中產生記憶體碎片。
  2. new的對象先放伊甸園區。此區有大小限制。
  3. 當伊甸園的空間填滿時,程式又需要創建對象,JVM的垃圾回收器將對伊甸園區進行垃圾回收(Minor GC),將伊甸園區中的不再被其他對象所引用的對象進行銷毀。再載入新的對象放到伊甸園區
  4. 然後將伊甸園中的剩餘對象移動到幸存者0區
  5. 如果再次觸發垃圾回收,此時上次幸存下來的放到幸存者0區的,如果沒有回收,就會放到幸存者1區。
  6. 如果再次經歷垃圾回收,此時會重新放回幸存者0區,接著再去幸存者1區
  7. 什麼時候能去養老區呢?可以設置次數。預設是15次。可以設置參數:-XX:MaxTenuringThreshold=N進行設置
  8. 在養老區,相對悠閑。當養老區記憶體不足時,再次觸發GC: Major GC,進行養老區的記憶體清理。
  9. 若養老區執行了Major GC之後發現依然無法進行對象的保存,就會產生OOM異常
java.lang.OutOfMemoryError: Java heap space

5.記憶體分配策略

5.1堆空間分代思想

  • 經研究,不同對象的生命周期不同。70%-99%的對象是臨時對象
  • 新生代:有Eden、兩塊大小相同的Survivor(又稱為from/to,s0/s1)構成to總為空。
  • 老年代:存放新生代中經歷多次GC仍然存活的對象。

通過將堆分代,可以將生命周期較短的對象放在年輕代中,而將生命周期較長的對象放在老年代中。這樣,垃圾回收器在回收時,可以針對不同代的對象採用不同的策略。對於年輕代中的對象,可以快速掃描和回收,而對於老年代中的對象,則需要經過多次垃圾回收後才能被回收。這種分代思想可以優化垃圾回收的效率,提高程式性能。

如果不分代,所有對象都放在同一代中,會導致垃圾回收效率變低,因為垃圾回收器需要掃描整個堆來查找需要回收的對象。同時,不分代也會導致記憶體浪費,因為一些對象雖然已經被釋放,但是它們的記憶體空間並沒有被回收。因此,分代是Java垃圾回收的重要優化策略,可以提高程式性能和可靠性。

5.2分配原則

  • 優先分配到Eden

  • 大對象直接分配到老年代,儘量避免程式中出現過多的大對象

  • 長期存活的對象分配到老年代

  • 動態對象年齡判斷如果survivor區中相同年齡的所有對象大小的總和大於survivor空間的一半,年齡大於或等於該年齡的對象可以直接進入老年代,無須等到MaxTenuringThreshold 中要求的年齡。

6.TLAB

TLAB(Thread Local Allocation Buffer)是Java虛擬機的一種記憶體分配優化技術。它為每個線程分配一塊私有的記憶體區域,稱為TLAB(Thread Local Allocation Buffer),使得每個線程都擁有自己的記憶體空間,從而避免多線程之間的記憶體競爭和同步問題,提高了記憶體分配的效率。因為堆區是線程共用區域,任何線程都可以訪問到堆區中的共用數據,對象實例的創建在JVM中非常頻繁,因此在併發環境下從堆區中劃分記憶體空間是線程不安全的

TLAB的大小是固定的,當TLAB用滿時,會新申請一個TLAB,而老TLAB里的對象還留在原地,無法感知自己是否是從TLAB分配出來的。當分配一次以後記憶體還是不夠時,會直接移入Eden區。

TLAB的優點是提高了記憶體分配的效率,減少了多線程之間的競爭和同步問題。但是,由於TLAB的大小是固定的,可能會出現浪費空間的情況,導致Eden區空間不連續,積少成多。因此,在創建大量對象時,應該考慮調整堆結構或使用對象池等技術來避免TLAB的缺點。

儘管不是所有的對象實例都能夠在TLAB中成功分配記憶體,但JVM確實是將TLAB作為記憶體分配的首選。

在程式中,開發人員可以通過選項“-XX:UseTLAB”設置是否開啟TLAB空間。

預設情況下,TLAB空間的記憶體非常小,僅占有整個Eden空間的1%,當然我們可以通過選項“-XX:TLABWasteTargetPercent”設置TLAB空間所占用Eden空間的百分比大小。

一旦對象在TLAB空間分配記憶體失敗時,JVM就會嘗試著通過使用加鎖機制確保數據操作的原子性,從而直接在Eden空間中分配記憶體。

7.堆空間常用的參數設置

oracle官網配置:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
-XX:+PrintFlagsInitial : 查看所有的參數的預設初始值
-XX:+PrintFlaqsFinal   :查看所有的參數的最終值 (可能會存在修改-XX:+PrintFlaqsFinal不再是初始值)
-Xms:初始堆空間記憶體 (預設為物理記憶體的1/64)
-Xmx:最大堆空間記憶體(預設為物理記憶體的1/4)
-Xmn:設置新生代的大小。(初始值及最大值)
-XX:NewRatio:配置新生代與老年代在堆結構的占比
-XX:SurvivorRatio:設置新生代中Eden和s0/S1空間的比例
-XX:+PrintGCDetails:輸出詳細的GC處理日誌
列印gc簡要信息
-XX:+PrintGC
-verbose:gc
-XX:HandlePromotionFailure: 是否設置空間分配擔保
-XX:MaxTenuringThreshold: 設置新生代垃圾的最大年齡

8.簡單講述幾種GC

JVM在進行GC時,並非每次都對上面三個記憶體(新生代、老年代;方法區)區域一起回收的,大部分時候回收的都是指新生代。

針對HotSpot VM的實現,它裡面的GC按照回收區域又分為兩大種類型:

部分收集(Partial GC):不是完整收集整個Java堆的垃圾收集。其中又分為:

  • 新生代收集 (Minor GC / Young Gc):只是新生代的垃圾收集
  • 老年代收集(Major Gc / old Gc):只是老年代的垃圾收集
  • 混合收集 (Mixed GC): 收集整個新生代以及部分老年代的垃圾收集。目前,只有G1 GC會有這種行為

一種是整堆收集 (Full GC):收集整個java堆和方法區的垃圾收集

8.1Minor GC(年輕代GC)

  • 當年輕代空間不足時,就會觸發Minor GC,這裡的年輕代滿指的是Eden代滿,Survivor滿不會引發GC(每次 Minor GC 會清理年輕代的記憶體)

  • 因為 Java 對象大多都具備朝生夕滅的特性,所以 Minor GC 非常頻繁,一般回收速度也比較快。

  • GC會引發STW,暫停其它用戶的線程,等垃圾回收結束,用戶線Minor程才恢復運行。

8.2Major GC(老年代GC)

出現了Major GC,經常會伴隨至少一次的Minor GC(但非絕對的,在ParallelScavenge收集器的收集策略里就有直接進行Major GC的策略選擇過程)。
也就是在老年代空間不足時,會先嘗試觸發Minor GC。如果之後空間還不足則觸發Major GC

Major GC的速度一般會比Minor G慢10倍以上,STW的時間更長,如果Major GC後,記憶體還不足,就報OOM了。

8.3Full GC

觸發FullGC 執行的情況有如下五種:

(1)調用System.gc()時,系統建議執行Full GC,但是不必然執行
(2)老年代空間不足
3)方法區空間不足
(4)通過minor GC後進入老年代的平均大小大於老年代的可用記憶體
(5)由Eden區、survivor space0 (From Space) 區向survivor space1(ToSpace) 區複製時,對象大小大於To Space可用記憶體,則把該對象轉存到老年代,且老年代的可用記憶體小於該對象大小
說明: full gc是開發或調優中儘量要避免的。這樣暫時時間會短一些

9.小結

  • 年輕代是對象的誕生、成長、消亡的區域,一個對象在這裡產生、應用,最後被垃圾回收器收集、結束生命。

  • 老年代放置長生命周期的對象,通常都是從survivor區域篩選拷貝過來的Java對象。當然,也有特殊情況,我們知道普通的對象會被分配在TLAB上;如果對象較大,JVM會試圖直接分配在Eden其他位置上;如果對象太大,完全無法在新生代找到足夠長的連續空閑空間,JVM就會直接分配到老年代

  • 當GC只發生在年輕代中,回收年輕代對象的行為被稱為MinorGC。當GC發生在老年代時則被稱為MajorGC 或者FulIGC。一般的,MinorGC 的發生頻率要比MajorGc高很多,即老年代中垃圾回收發生的頻率將大大低於年輕代。

其實堆空間並不是對象分配的唯一選擇,隨著JIT編譯期的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會導致一些微妙變化,下一節寫關於逃逸分析的一些技術


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • > ## ESP32連接MQ Sensor實現氣味反應 > > > ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/1961512734a74deb9d455b59cb1ccdab.png#pic_center) > > > | [ESP32+MQTT+MySQL實 ...
  • > 在 MySQL 中,GROUP BY用於將具有指定列中相同值的行分組在一起。這是在處理大量數據時非常有用的功能,允許對數據進行分類和聚合。 # 基本使用 ## 語法 以下是GROUP BY子句的基本語法: """ SELECT col1, col2, ..., aggregate_functio ...
  • 博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 背景 最近聽音樂的時候,看到各種動效,突然好奇這些音頻數據是如何獲取並展示出來的,於是花了幾天功夫去研究相關的內容,這裡只是給大家一些代碼實例,具體要看懂、看明白,還是建議大家大家結合相關API文檔來閱讀這篇文章。 參考資料地址:Web ...
  • 第1代圖片驗證碼 - 字母數字型 第2代滑動驗證碼 - 圖片截取型 第3代驗證碼 - 選圖型 vercode.js 結合了上面的情況下新研發的一種驗證碼。 驗證碼類型 驗證碼描述 操作性 安全性 描述 字母數字型圖片驗證碼 這是一種通過後臺隨機碼生成圖片的驗證碼。伺服器會在隨機碼生成時保存隨機碼。 ...
  • Java三大特征:封裝,繼承和多態成員變數:靜態成員變數(static)和實例成員變 訪問方法:類名.靜態成員變數;對象.實例成員變數;對象.靜態成員變數;(第三個不推薦) 套話:靜態的都可以訪問,實例的只能實例的訪問 繼承中子類不能繼承父類的構造方法,eg:父類:public People(int ...
  • # 1.網站的入口--路由和視圖 URL是網站Web服務的入口。用戶在瀏覽器輸入URL發出請求後,django會根據路由系統,運行對應的視圖函數,然後返回信息到瀏覽器中。 ## 1.1 認識路由 創建項目時,會自動生成urls.文件,文件中定義了項目的路由信息,成為項目的路由解析入口。在自建的應用中 ...
  • 操作系統 : debian 11 (bullseye,docker)、Windows10_x64 FreeSWITCH版本 :1.10.9 Docker版本:23.0.6 Python 版本 : 3.9.2 日常工作中,有時候會遇到g729編碼的相關內容,但FreeSWITCH預設是不支持g729編 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...