JVM總結

来源:https://www.cnblogs.com/chuan2/archive/2023/02/27/17162192.html
-Advertisement-
Play Games

JVM總結 1. 記憶體結構 線程私有區 程式計算器 作用:是一塊較小的記憶體空間,存儲的是當前線程所執行的位元組碼文件的序號 特點:線程私有,不會出現記憶體空間溢出 虛擬機棧 虛擬機棧是管理JAVA方法執行的記憶體模型,每個方法執行時都會創建一個當前棧楨,在當前棧楨裡面存儲方法的局部變數表,操作數棧,動態鏈 ...


JVM總結

目錄

1. 記憶體結構

image-20230225154307114

線程私有區

程式計算器

  • 作用:是一塊較小的記憶體空間,存儲的是當前線程所執行的位元組碼文件的序號
  • 特點:線程私有,不會出現記憶體空間溢出

虛擬機棧

虛擬機棧是管理JAVA方法執行的記憶體模型,每個方法執行時都會創建一個當前棧楨,在當前棧楨裡面存儲方法的局部變數表,操作數棧,動態鏈接方法,返回值,返回地址等信息,棧大小決定了方法調用的可達深度(遞歸多少層,嵌套調用多少層其他方法,在idea中,-Xss參數可以設置虛擬機棧的大小)

  • 是線程私有的
  • 局部變數表存放了編譯期可知的所有基本數據類型(byte,short,int,long,float,double,boolean,char),以及對象引用
  • 棧太小或者方法調用過深都將拋出StackOverFlowError異常

本地方法棧

為本地語言服務的棧,Native方法服務

線程共用區

堆記憶體

存放對象實例的區域,對象,數組,以及常量池(從java7開始常量池也會使用堆記憶體)

堆記憶體從GC角度可以分為:新生類(Eden區,From Survision區和To Survision區),老年代,永久代(在Java8的時候被移除了)

特點:是線程貢獻,需要考慮線程安全問題,同時會產生記憶體溢出問題

-Xms 設置最小堆記憶體大小(不能小於1024K)

-Xmx:設置最大堆記憶體大小(不能小於1024K)

方法區

用於存儲已被虛擬機載入的類信息,常量,靜態變數,即時編譯器編譯後代碼等數據

image-20230227220649797

特點:

  • 是一塊線程共用的記憶體區域
  • 方法區的大小決定了系統可以保存多少個類,如果系統定義了太多的類,導致方法區溢出,虛擬機同樣會拋出記憶體溢出的錯誤
  • 現在說的方法區一般是指元數據區(元空間Metaspace,java8的時候添加的),如果不指定大小,預設情況下,虛擬機會耗盡系統的可用記憶體

堆棧的區別

  • 存儲的東西不同,棧記憶體存儲局部變數和方法調用,而堆記憶體存儲對象,包括成員變數,局部變數,還有類變數
  • 共用不同,棧記憶體是線程私有的,堆記憶體是所有線程共有的
  • 異常錯誤不同:棧空間不足:java.lang.StackOverFlowError,堆空間不足:java.lang.OutOfMemoryError。
  • 大小不同,棧空間小於堆空間

獲取堆記憶體數據

java.lang.Runtime類中包含了與記憶體先關的方法

獲取剩餘空間的的位元組數:Runtime.freeMemory()

獲取總記憶體的位元組數:Runtime.totalMenory()

2. 垃圾回收

垃圾回收機制

在Java中,程式員不需要顯示的去釋放一個對象的記憶體,而是由虛擬機自動去執行,在JVM中,有一個垃圾回收線程,他是低優先順序的,在正常情況下是不會執行的,只有在虛擬機空閑或者在堆記憶體不足時才會觸發執行,掃描那些被視為垃圾的對象,將他們添加到回收的集合中進行回收,也可以進行手動回收,通過System.gc(),通知GC運行

垃圾回收演算法

  • 標記清除法

    標記出所有需要回收的對象,在標記完成後,統一回收的被標記的對象

    優點:速度比較快

    缺點:會產生記憶體碎片,碎片過多,仍會使得連續空間變少

  • 標記整理

    標記出所有需要回收的對象,在標記完成後統一進行整理,將存活對象進行一端移動,減少記憶體碎片,效率相對較低

    優點:無記憶體碎片

    缺點:效率較低

  • 複製演算法

    開闢兩份大小相等的空間,一份空間始終空著,垃圾回收時將存活對象拷貝進空閑時間

    優點:無記憶體碎片

    缺點:占用空間多

  • 分代回收

根據對象的存活周期不同,將對象劃分為幾塊,比如堆記憶體的新生代和老年代,然後根據各個年代的特點採用最合適的演算法進行回收;

新生代對象的存活時間比較短,因此使用的是複製演算法,老年代對象存活的時間比較長,因此使用的是標記清除或者標記整理

分代垃圾回收的過程

分代垃圾回收器分兩個區,新生代和老年代,新生代預設占1/3,老年代占2/3

新生代使用複製演算法,新生代裡面又分3個區(Eden,To Survivor, From Survicior)預設占比是8:1:1,當Eden區滿了之後就會觸發第一次MinorGC,將Eden區和From區存活的對象複製到To Survivor區域,然後to Survivor區域和From Survivor互換,原來的To Servivor區域成為下一次的From Survivor區域,然後清空Eden和From Survivor區中的對象,From中的對象每經過一次MinorGC,他的年齡值就會加1,達到15的移動到老年代,這裡使用了複製演算法,老年代滿了或者是超過了臨界值則會觸發完全垃圾回收

判斷一個對象是否為垃圾

引用計數法

堆中每個對象實例都有一個引用計數,當一個對象被創建的時候,且將這個對象實例分配給一個變數,該變數計數設置為1,當任何其他變數被賦值為這個對象的引用時,計數加1,但當一個對象實例的某個引用超過了生命周期,或者被設置為一個新值時,這個對象實例的引用計數將會減1,任何引用計數為0的對象都是可以被當做垃圾收集的對象,也就是一個垃圾,引用計數法容易產生迴圈引用的問題,如果兩個對象相互引用,那麼他們的引用就會一直存在,導致一直無法回收,為瞭解決這個問題,下麵引入了可達性分析法

可達性分析演算法

可達性分析演算法又叫做根搜索法,就是通過一系列的稱之為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索走過的路徑被稱為(Reference Chain)影響鏈,當一個對象到GC Roots沒有任何鏈相連時(即從 GC Roots節點到該節點是不可達的),則這個對象就是可以被當做垃圾收集的對象,也就是一個垃圾

可以被作為GC Roots的對象

  • 虛擬機棧中引用的對象
  • 方法區靜態成員的引用對象
  • 方法區常量的引用對象
  • 本地方法棧引用的對象

引用類型

  • 強引用

    預設聲明的就是強引用,只要強引用存在,垃圾回收器就永遠不會回收該對象,哪怕記憶體不足時也不會去回收,所以強引用是造成Java記憶體泄漏的主要原因,如果想回收某個對象可以將值賦值為null,切斷強引用關係

  • 軟引用:一些非必需但是仍有用的對象,通過SoftReference實現,在記憶體足夠的時候,軟引用對象不會回收,在記憶體不足時則會回收,當回收了軟引用對象記憶體還是不足時,會拋出記憶體溢出異常

  • 弱引用:比軟引用的引用強度更低一些,通過WeakReference實現,無論記憶體是否足夠,JVM都會被進行垃圾回收

  • 虛引用:最弱的引用關係,通過PhantomReference類實現,每次垃圾回收都會被回收,主要用於跟蹤對象的垃圾回收狀態,虛引用的對象始終返回一個null,虛引用對象始終和引用隊列一起使用,當一個對象還存在虛引用時,會被加入到引用隊列中,jvm通過引用隊列是否包含這個虛引用來瞭解這個對象是否將要被進行垃圾回收

3. 對象分配

對象在記憶體中如何分配

對象優先在Eden區中分配,當Eden區沒有足夠的空間時,會觸發垃圾回收,把存活的對象移動到Survicor空間,如果Survivor空間也滿了,則會把部分對象移動到老年代中,如果對象太大,是一個大對象,則這個對象會直接分配到老年代,不會發生GC

對象如何從年輕代進入老年代

  • 對象年齡夠老,預設是15,可以通過XX:MaxTenuringThreshold設置,複製到老年代,同時年齡加1
  • 大對象會直接分配到老年代,大對象的定義和具體的JVM版本,堆大小,垃圾回收策略有關,一般為2K~128k,可以通過XX:pretenureSizeThreshold設置其大小

4. 垃圾收集器

JVM針對新生代和老年代分別提供了不同的垃圾收集器,新生代有Serial,ParNew,Parallel Scavenge,針對老年代提供的垃圾收集器有Serial Old,Parallel Old,CMS,還有針對不同區域的G1分區收集演算法

常見的垃圾收集器

Serial垃圾收集器

Java虛擬機運行在Client模式下的新生代的預設垃圾收集器,基於複製演算法實現,是一個單線程收集器,在進行垃圾收集時,必須暫停其他所有工作線程,直到垃圾收集結束,Serial收集器對於但CPU運行環境來說,沒有線程交互開銷,可以獲得最高的單線程垃圾收集效率

ParNew垃圾收集器

Java虛擬機運行在Server模式下,新生代的預設垃圾收集器,基於複製演算法實現,採用多線程模式工作,在進行垃圾收集時,必須暫停其他所有工作線程,直到垃圾收集結束,ParNew垃圾收集器預設開啟與CPU同等數量的線程進行垃圾回收,可以通過-XX:ParallelGCThreads參數調節工作線程數

Parallel Scavenge垃圾收集器

Parallel Scavenge垃圾收集器是為提高新生代垃圾收集效率而設計的垃圾收集器,基於複製演算法實現,採用多線程,在系統吞吐量上有很大的優化,,可以更高效的利用CPU完成垃圾回收任務,Parallel Scavenge提供了三個參數用於調節,控制垃圾回收的停頓時間及吞吐量,分別是:控制足最大垃圾收集停頓時間:-XX:MaxGCPauseMillis,控制吞吐量大小:-XX:GCTimeRation,控制是否開啟自適應調節策略:UseAdaptiveSizePolicy

Serial Old垃圾收集器

Serial Old是JVM運行在Client模式下,老年代的預設垃圾收集器,Serial Old是Serial的老年代實現,同Serial一樣採用單線程執行,不同的是Serial Old基於標記整理演算法實現

Parallel Old垃圾收集器

Parallel Old垃圾收集器採用多線程併發進行垃圾回收,基於標記整理法實現,在設計上優先考慮系統吞吐量,其次考慮停頓時間等因素

CMS垃圾收集器

CMS垃圾收集器是為老年代設計的垃圾收集器,其主要目的是達到最短時間的垃圾回收停頓時間,基於線程的標記清除演算法實現

CMS的垃圾回收過程

  • 初始標記:只標記GC Roots直接關聯的對象,速度很快,需要暫停所有工作線程
  • 併發標記:和用戶一起工作,執行GC Roots跟蹤標記過程,不需要暫停工作線程
  • 重新標記:為了確保併發標記的正確性,重新標記已經標記的對象,需要暫停工作線程
  • 併發清除:和用戶線程一起工作,執行清除GC Roots不可達對象的任務,不需要暫停工作線程

G1垃圾收集器

G1垃圾收集器是為了避免全區域垃圾收集引起的系統停頓,將堆記憶體劃分大小固定的幾個獨立區域,獨立使用這些區域跟蹤垃圾收集進度,同時在維護一個優先順序列表,根據系統允許的最長垃圾收集時間,優先回收垃圾最多的區域,相對於CMD垃圾收集器,G1垃圾收集器不產生記憶體碎片,可以精確的控制停頓時間,在不犧牲吞吐量的前提下實現停頓垃圾回收

G1垃圾回收器參數

  • -XX:MaxGCPauseMillis:暫停毫秒級,預設200毫秒
  • -XX:G1HeapRegionSize:區域大小,預設最多2048塊,每塊的大小需要為2的冪次方,最大為32M
  • -XX:G1NewSizePercent:新生代的最低百分比,預設5%
  • -XX:G1MaxNewSizePercent:新生代的最大百分比,預設60%

5. 類載入器

類載入器負責將class文件載入到java虛擬機中,併為之創建一個Class對象

類載入器的流程

  • 載入:將class文件載入到記憶體中,將靜態數據結構轉化成方法區中運行時的數據結構,在堆中生成一個代表這個類的對象,作為數據訪問的入口
  • 驗證:確保載入的類符合JVM規範和安全,保證被效驗類的方法在運行時不會做出危害虛擬機的事件,做一個安全檢查
  • 準備:為static變數在方法區中分配記憶體空間,設置變數的初始值,註意只設置靜態變數,不包括實例變數,實例變數在對象初始化時賦值
  • 解析:虛擬機將常量池內的符號引用替換為直接引用的過程,符號引用中的符號可以是任何形式的字面量,只要能無歧義的定位到目標即可,直接引用是可以直接指向目標的指針,相對偏移量或者間接定位到目標的句柄
  • 初始化:初始化類變數和其他資源
  • 使用
  • 卸載:GCh將對象從記憶體中卸載

常用的類載入器

  • 啟動類載入器(Bootstrap ClassLoader):虛擬機內置類載入器,載入java核心類庫,如JAVA_HOME/jre/lib/rt.jar,resources.jar,sun.boot.class.path,使用C+/C++實現,他沒有父類載入器,是擴展類載入器和應用程式載入器的父類載入器
  • 擴展類載入器(Extension classLoader):由java語言編寫,從系統屬性java.ext.dirs目錄中或者JDK安裝目錄JRE/lib/ext載入類庫
  • 應用程式類載入器(Application ClassLoader):應用程式類載入器,負責載入用戶類路徑上所指定的類,我們程式中預設的類載入器,可以通過ClassLoader#getSystemClassLoader()獲取並操作這個載入器

雙親委派機制

雙親委派機制是當一個類載入器需要載入一個位元組碼文件時,首先會把這個任務委托給他的上級類載入器,上級類載入器又交給他的上級,以此遞歸,直到上級類載入器不能載入該位元組碼文件,然後再自己去載入這個位元組碼文件,如果自己也無法載入則拋出ClassNotFoundException異常,這樣做的好處是防止一個位元組碼文件多次載入,保證了數據的安全性,即使重覆載入了也不會是同一個class對象

6. JVM調優

在調優之前要先明確是否需要使用JVM調優,因為大多數Java應用是不需要調優的,大多數導致GC問題的原因是代碼層面的問題,比如創建的對象數量過多,使用了大量的全局變數和大對象

需要調優的場景

  • Heap記憶體(老年代)持續上漲,達到設置的最大記憶體值
  • Full GC次數頻繁
  • GC停頓時間過長
  • 應用出現OutOfMemory等記憶體異常
  • 系統吞吐量不高

調優步驟

  • 分析GC日誌和Demp文件,判斷是否需要優化,確定瓶頸問題點
  • 確定JVM調優量化目標
  • 調整JVM調優參數,包括記憶體,延遲,吞吐量
  • 觀察調優前後的差異,不斷分析和調整,找到最合適的參數
  • 將這些參數應用到伺服器

調優參數

語法

  • -XX:+ ' +'表示啟用該選項
  • -XX:- '-'表示關閉該選項
  • -XX:= '='給選項設置一個數字類型的值,可跟隨單位,例如:m和M表示兆位元組,k和K表示k位元組,g和G表示千兆位元組

參數示例

-Xms4g:設置最小堆記憶體大小為4g,堆記憶體初始大小
-Xmx4g:設置最大堆記憶體大小為4g,堆記憶體最大值
-Xmn1200m:設置年輕代大小為1200MB,增大年輕代後,將會減小老年代的大小,這個值對系統性能較大,官方推薦配置為整個堆的3/8
-Xss512K:設置每個線程的堆棧大小,JDK5.0後每個線程堆棧大小為1MB,以前為256K,值越小,能創建的線程越多
-XX:NewRatio=4:設置年輕代與老年代的比值為1:4,年輕代占整個堆棧的1/5
-XX:SurvivorRatio=8:設置年輕代中Eden區和Survivor區的比值為2:8,一個Survivor區占整個年輕代的1/10
-XX:MaxTenuringThreshold=15:設置Survivor區中對象年齡的最大值為15,超過這個值則進入老年代,如果設置為0,則年輕代不經過Survivor,直接進入老年代
-XX:+UseParNewGC:年輕代
-XX:+UseConcMarkSweepGC:老年代

常用調優參數

-Xms:初始化堆記憶體大小,預設為物理記憶體的1/64(小於1G)
-Xmx:堆記憶體的最大值。預設(MaxHeapFreeRation參數可以調整)空餘堆記憶體大於70%時,JVM會減少堆直到-Xms的最小限制
-Xmn:新生代大小,包括Eden和兩個Survivor區
-XX:MaxDirectMemorySize=1G:直接記憶體,報java.lang.OutOfMemoryError:Direct buffer memory異常時可以上調這個值
-XX:+DisableExplicitGC:禁止運行期間顯示的調用System.gc()來觸發full GC
-XX:CMSlnitiatingOccupancyFraction=60:老年代記憶體回收閾值,預設為68
-XX:ConcGCThreads=4:CMS垃圾回收器並行線程數,推薦值為CPU核心數
-XX:ParallelGCThreads=8:新生代並行收集器的線程數
-XX:MaxTenuringThreshold=10:Survivor區的最大年齡值,超過就會進入老年代

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

-Advertisement-
Play Games
更多相關文章
  • 初識 WebGL 什麼是 WebGL webgl 在支持 canvas 的瀏覽器中進行 2d 或 3d 渲染。 webgl 程式除了有 Html、javascript,還需要加入著色器語言(GLSL ES)。 WebGL 使得網頁在支持 HTML <canvas> 標簽的瀏覽器中,不需要使用任何插件 ...
  • 前言 403 頁面通常表示無許可權訪問,與 404 頁面代表著不同含義。而大部分管理後臺框架僅提供了 404 頁面的支持,但卻忽略了對 403 頁面的處理,有的框架雖然也有對 403 頁面的處理,但處理效果卻不盡人意。 那怎麼樣的 403 頁面才是即好用,又優雅呢? 其他框架是怎麼做的 1、完全不處理 ...
  • 1. 異常 1.1. 代碼應該僅在發生意料之外的事情時拋出異常 1.1.1. 防禦性編程性能好 1.2. 異常的處理成本未必很高 1.2.1. 應該只在適當的時候使用 1.2.2. 棧越深,處理異常的成本就越高 1.3. 對於頻繁創建的系統異常,JVM會優化獲取棧軌跡的性能開銷 1.4. 在異常中禁 ...
  • 動態SQL語句 1.基本介紹 官方文檔 mybatis – MyBatis 3 | 動態 SQL 為什麼需要動態SQL? 動態SQL是MyBatis的強大特性之一 使用 JDBC 或其他類似的框架,根據不同條件拼接SQL語句非常麻煩,例如拼接時要確保不能忘記添加必要的空格,還要註意去掉列表最後一個列 ...
  • 原味地址 https://haiyux.cc/2023/02/26/k8s-client-go/ client-go是什麼? client-go是Kubernetes官方提供的Go語言客戶端庫,用於與Kubernetes API伺服器交互。使用client-go,您可以編寫Go語言程式來創建、修改和 ...
  • 通常情況下,部署Django應用到生產環境時都會通過uwsgi部署,uwsgi一些配置項配置問題有可能會導致服務出現502狀態碼或者其他超時等的情況 常用到的配置項如下: reload-on-as = 600 reload-on-rss = 500 evil-reload-on-rss = 800 ...
  • 我們服務啟動時,sybase資料庫 連接直接創建10個連接。(為什麼啟動時會創建這麼多連接?) 有時候可以寫入sybase庫,大部分寫入失敗 查詢sybase庫數據可以查出來 ,沒問題 嘗試的方案1 如圖: Springboot 連接迪砂資料庫 的application.yml 配置文件 我們配置的 ...
  • 首碼和 首碼和,顧名思義,就是所有首碼之和,給一個最基本的例子: 如圖,a為原始數組,s為完成預處理後的數組,很容易看出來s[ i ]=s[ i - 1 ]+a[ i ],而也就是s[ i ]=a[1]+a[2]+……+a[ i ],需要註意的是記s[0]=0。 那麼,如果我想要知道一個區間的區間和 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...