成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入了解Java的GC原理,掌握JVM 性能調優!

開發 后端
JVM性能調優是一個復雜的過程,需要結合具體的應用程序特性和需求來進行調優。不同的應用場景可能需要不同的調優策略。

對于 Java 開發人員來說,進行程序的性能優化是很有挑戰的工作,也是很有意義的一件事。本篇主要根據 JVM 內存模型和垃圾回收的詳細講解,可以更好的理解JVM的調優的根本原理。

JVM內存模型

JVM 架構

  • 類加載器(Classloader):類加載器是JVM的一個子系統,用于加載類文件。每當我們運行java程序時,它首先由類加載器加載。
  • 類(方法)區(Class(Method) Area):類(方法)區存儲每個類的結構,例如運行時常量池、字段和方法數據、方法的代碼。
  • 堆(Heap):是分配對象的運行時數據區域。
  • 堆棧(Stack):Java 堆棧存儲幀。它保存局部變量和部分結果,并在方法調用和返回中發揮作用。每個線程都有一個私有的 JVM 堆棧,與該線程同時創建。每次調用方法時都會創建一個新框架。當其方法調用完成時,框架將被銷毀。
  • 程序計數器寄存器(PC):PC(程序計數器)寄存器包含當前正在執行的Java虛擬機指令的地址。
  • 本機方法堆棧(Native Method Stack):它包含應用程序中使用的所有本機方法。
  • 執行引擎(Execution Engine):它包含:一個虛擬處理器;解釋器:讀取字節碼流然后執行指令。
  • Just-In-Time(JIT)編譯器:它用于提高性能。JIT 同時編譯具有相似功能的字節碼部分,從而減少編譯所需的時間。這里,術語“編譯器”是指從Java虛擬機(JVM)的指令集到特定CPU的指令集的翻譯器。
  • Java 本機接口:Java 本機接口 (JNI) 是一個框架,提供與用其他語言(如 C、C++、匯編等)編寫的另一個應用程序進行通信的接口。Java 使用 JNI 框架將輸出發送到控制臺或與操作系統交互。

應該已經使用了一些像這樣的 JVM 配置

JAVA_OPTS=”-server -Xms2560m -Xmx2560m -XX:NewSize=1536m -XX:MaxNewSize=1536m -XX:MetaspaceSize=768m -XX:MaxMetaspaceSize=768m -XX:InitialCodeCacheSize=64m -XX:ReservedCodeCacheSize=96m -XX:MaxTenuringThreshold=5″
  • -server - 啟用“ServerHotspotVM”;該參數在 64 位 JVM 中默認使用。
  • -Xms - 堆的初始空間。
  • -Xmx - 堆的最大空間。
  • -XX:NewSize - 初始新空間。將新大小設置為總堆的一半通常比使用較小的新大小提供更好的性能。
  • -XX:MaxNewSize - 最大新空間。
  • -XX:MetaspaceSize - 靜態內容的初始空間。
  • -XX:MaxMetaspaceSize - 靜態內容的最大空間。
  • -XX:InitialCodeCacheSize - JIT 編譯代碼的初始空間。代碼緩存太小(默認為 48m)會降低性能,因為 JIT 無法優化高頻方法。
  • -XX:ReservedCodeCacheSize - JIT 編譯代碼的最大空間。
  • -XX:MaxTenuringThreshold - 在升級到老年代空間之前,將幸存者保留在幸存者空間中最多 15 次垃圾回收。

那么 JVM 是如何駐留在內存上的?JVM 消耗主機操作系統內存上的可用空間。

然而,在 JVM 內部,存在獨立的內存空間(堆、非堆、緩存),以存儲運行時數據和編譯后的代碼。

堆內存

  • 堆分為兩部分:Young Generation 和 Old Generation
  • JVM 啟動時分配堆(初始大小:-Xms)
  • 應用程序運行時堆大小增加/減少
  • 堆的最大空間:-Xmx

以下是有關服務器應用程序堆大小的一般準則:

  • 除非遇到暫停問題,否則請嘗試為虛擬機授予盡可能多的內存。默認大小通常太小。
  • 將-Xms和-Xmx設置為相同的值可以消除虛擬機中最重要的大小調整決策,從而提高可預測性。但是如果設置相同大小出了錯誤,虛擬機將無法進行補償。
  • 一般來說,隨著處理器數量的增加,內存也會隨之增加,因為分配可以并行進行。

年輕代(Young Generation)

  • 這是為包含新分配的對象而保留的
  • Young Gen 包括三個部分——Eden Memory 和兩個 Survivor Memory 空間(S0、S1)
  • 大多數新創建的對象都會進入Eden space。
  • 當 Eden 空間充滿對象時,將執行 Minor GC(又名 Young Collection),并將所有幸存者對象移動到幸存者空間之一。
  • Minor GC 還會檢查幸存者對象并將它們移動到其他幸存者空間。所以在某一時刻,幸存者的一個空間總是空著的。
  • 經過多次GC后幸存的對象會被移至Old代內存空間。通常,這是通過在年輕代對象有資格晉升到老年代之前設置年齡閾值(-XX:MaxTenuringThreshold)來完成的。

老年代(Old Generation)

  • 這是為包含在多輪 Minor GC 后仍能存活的長壽命對象而保留的
  • 當 Old Gen 空間滿時,將執行 Major GC(又名 Old Collection)(通常需要更長的時間)

非堆內存

  • 這包括永久生成(自 Java 8 起被 Metaspace 取代)
  • Perm Gen 存儲每個類的結構,例如運行時常量池、字段和方法數據、方法和構造函數的代碼以及內部字符串
  • 可以使用 -XX:PermSize 和 -XX:MaxPermSize 更改其大小

高速緩存存儲器

  • 這包括代碼緩存
  • 存儲JIT編譯器生成的編譯代碼(即本機代碼)、JVM內部結構、加載的分析器代理代碼和數據等。
  • 當代碼緩存超過閾值時,它會被刷新(GC 不會重新定位對象)。

什么是GC?

Java 通過一個稱為垃圾收集器的程序提供自動內存管理。

“移除不再使用的對象。”

上面的一切都是在堆中完成的,堆是運行時動態內存分配的空間,用于包含所有 java 對象。除了堆之外,還有堆棧,其中包含支持線程執行的局部變量和函數調用。

Java 垃圾收集的實際工作原理

許多人認為垃圾收集會收集并丟棄死對象。事實上,Java 垃圾收集的作用恰恰相反!活動對象被跟蹤,其他所有對象都被指定為垃圾。這種根本性的誤解可能會導致許多性能問題。

讓我們從堆開始,它是用于動態分配的內存區域。在大多數配置中,操作系統會提前分配堆,以便在程序運行時由 JVM 管理。這有幾個重要的影響:

  • 對象創建速度更快,因為不需要每個對象都與操作系統進行全局同步。分配只是聲明內存數組的某些部分并將偏移指針向前移動(參見圖 2.1)。下一個分配從此偏移量開始,并聲明數組的下一部分。
  • 當不再使用某個對象時,垃圾收集器會回收底層內存并將其重新用于將來的對象分配。這意味著沒有顯式刪除,也沒有內存返回給操作系統。

新對象簡單地分配在已用堆的末尾

一旦某個對象不再被引用并且因此應用程序代碼無法訪問該對象,垃圾收集器就會將其刪除并回收未使用的內存。

垃圾收集根——所有對象樹的來源

每個對象樹必須有一個或多個根對象。只要應用程序可以到達這些根,那么整棵樹都是可以到達的。但是這些根對象什么時候被認為是可達的呢?稱為垃圾收集根,它是特殊對象始終是可訪問的,任何在其根處具有垃圾收集根的對象也是如此。

Java中有四種GC root:

  • 局部變量通過線程的堆棧保持活動狀態。這不是真實的對象虛擬引用,因此不可見。無論如何,局部變量都是 GC 根。
  • 活動的 Java 線程始終被視為活動對象,因此是 GC 根。這對于線程局部變量尤其重要。
  • 靜態變量由它們的類引用。這一事實使它們成為事實上的 GC 根。類本身可以被垃圾收集,這將刪除所有引用的靜態變量。
  • JNI 引用是本機代碼作為 JNI 調用的一部分創建的 Java 對象。這樣創建的對象會被特殊對待,因為 JVM 不知道它是否被本機代碼引用。

GC 根是 JVM 本身引用的對象,因此可以防止其他所有對象被垃圾收集。

因此,一個簡單的 Java 應用程序具有以下 GC 根:

  • main方法中的局部變量
  • 主線程
  • 主類的靜態變量

標記并清除垃圾

標記可達對象

為了確定哪些對象不再使用,JVM 間歇性地運行所謂的“標記和清除”算法。正如所直覺的,這是一個簡單的兩步過程:

  • 該算法從 GC 根開始遍歷所有對象引用,并將找到的每個對象標記為活動對象。
  • 所有未被標記對象占用的堆內存都會被回收。它只是被簡單地標記為空閑,基本上清除了未使用的對象。

活動對象在上圖中表示為藍色。當標記階段結束時,每個活動對象都被標記。因此,所有其他對象(上圖中的灰色數據結構)都無法從 GC 根訪問,這意味著應用程序無法再使用無法訪問的對象。此類對象被視為垃圾,GC 應在以下階段中刪除它們。

標記階段需要注意以下重要方面:

  • 需要停止應用程序線程才能進行標記,因為如果圖表一直在不斷變化,就無法真正遍歷圖表。當應用程序線程暫時停止以便 JVM 可以進行內務活動時,這種情況稱為安全點,導致 Stop The World 暫停。安全點可以因不同的原因而被觸發,但垃圾收集是迄今為止引入安全點的最常見原因。
  • 此暫停的持續時間既不取決于堆中對象的總數,也不取決于堆的大小,而是取決于活動對象的數量。因此增加堆的大小并不會直接影響標記階段的持續時間。
  • 當標記階段完成后,GC就可以進行下一步并開始刪除不可達的對象。

刪除未使用的對象

對于不同的 GC 算法,未使用對象的刪除略有不同,但所有此類 GC 算法都可以分為三步:清除(sweeping)、壓縮(compacting)和復制(copying)。

Sweep

標記和清除算法在概念上使用最簡單的垃圾處理方法,即忽略此類對象。這意味著在標記階段完成后,未訪問對象占用的所有空間都被視為空閑,因此可以重用以分配新對象。

該方法需要使用所謂的空閑列表記錄每個空閑區域及其大小。空閑列表的管理增加了對象分配的開銷。這種方法還有另一個弱點——可能存在大量空閑區域,但如果沒有一個區域足夠大來容納分配,分配仍然會失敗(在 Java 中會出現 OutOfMemoryError 錯誤)。

它通常被稱為標記-清除算法。

Compact

Mark-Sweep-Compact算法通過將所有標記的對象 (即活動的對象)移動到內存區域的開頭來解決Mark-and-Sweep算法的缺點。這種方法的缺點是增加了GC暫停時間,因為我們需要將所有對象復制到一個新位置,并更新對這些對象的所有引用。Markand Sweep的好處也是顯而易見的--在這樣一個壓縮操作之后,通過指針碰撞,新對象的分配再次變得非常便宜。使用這種方法,空閑空間的位置總是已知的,也不會觸發碎片問題。

它通常被稱為標記-壓縮算法。

Copy

標記和復制算法非常類似于標記和壓縮,因為它們也重新定位所有活動對象。重要的區別在于,對象搬遷的目標是不同的記憶區域,作為幸存對象的新家。標記和復制方法具有一些優點,因為復制可以與標記在同一階段同時發生。缺點是需要多一個內存區域,該內存區域應該足夠大以容納幸存的對象。

它通常被稱為標記復制算法。

停止世界 (STW)

所有垃圾收集都是“Stop the World”事件。這意味著所有應用程序線程都將停止,直到操作完成。垃圾收集始終是“Stop the World”事件。

老年代用于存儲長期存活的對象。通常,為年輕代對象設置一個閾值,當達到該年齡時,該對象將被移動到老年代。最終需要收集老年代。此事件稱為主垃圾收集。

主垃圾收集也是 Stop the World 事件。通常,主垃圾收集要慢得多,因為它涉及所有活動對象。因此,對于響應式應用程序,應最大程度地減少主要垃圾收集。另請注意,主要垃圾收集的 Stop the World 事件的長度受到用于老年代空間的垃圾收集器類型的影響。

GC 可視化過程

當應用程序啟動并在 Eden 空間上分配內存時。藍色是活動對象,灰色是死對象(無法到達)。當給定空間已滿時,應用程序嘗試創建另一個對象,并且 JVM 嘗試在 Eden 上分配某些內容,但分配失敗。這實際上會導致輕微GC。

第一次minor GC后,所有存活對象將被移動到Survivor 1,年齡為1,死亡對象將被刪除。

應用程序正在運行,新對象再次在 Eden 空間中分配。有些對象在 Eden 空間和 Survivor 1 上都變得無法訪問

在第二次 Minor GC 之后,所有存活對象將被移動到 Survivor 2(來自年齡為 1 的 Eden 和年齡為 2 的 Survivor 1),并且死亡對象將被刪除。

應用程序仍在運行,新對象在 Eden 空間上分配,過了一會兒,一些對象從 Eden 和 Survivor 2 都無法訪問

在第三次minor GC之后,隨著年齡的增加,所有存活對象將從Eden和Survivor 2移動到Survivor 1,并且死亡對象將被刪除。

在Survivor中存活時間較長的對象,如果年齡大于-XX:MaxTenuringThreshold,將會被提升到老年代(Tuner)

我們可以使用 VisualVM 的插件 VisualGC 附加到已檢測的 HotSpot JVM,收集并以圖形方式顯示垃圾收集、類加載器和 HotSpot 編譯器性能數據。

性能基礎知識

通常,在調整 Java 應用程序時,重點是兩個主要目標之一:響應速度吞吐量

響應速度

響應能力是指應用程序或系統響應所請求的數據的速度。示例包括:

  • 桌面 UI 響應事件的速度有多快
  • 網站返回頁面的速度有多快
  • 返回數據庫查詢的速度有多快

對于注重響應能力的應用程序來說,較長的暫停時間是不可接受的。重點是在短時間內做出響應。

吞吐量

吞吐量側重于在特定時間段內最大化應用程序的工作量。如何測量吞吐量的示例包括:

  • 在給定時間內完成的交易數量。
  • 批處理程序在一小時內可以完成的作業數。
  • 一小時內可以完成的數據庫查詢數量。

對于注重吞吐量的應用程序來說,較長的暫停時間是可以接受的。由于高吞吐量應用程序關注較長時間段的基準,因此不考慮快速響應時間。

GC 有哪些類型?

并發標記清除 (CMS) 垃圾收集

CMS垃圾收集本質上是升級的標記和清除算法。它使用多個線程掃描堆內存。它經過修改以利用更快的系統并增強了性能。

它嘗試通過與應用程序線程同時執行大部分垃圾收集工作來最大程度地減少由于垃圾收集而導致的暫停。它在年輕代中使用并行的 stop-the-world 標記復制算法,在老年代中使用大多數并發的標記清除算法。

要使用 CMS GC,請使用以下 JVM 參數:

-XX:+UseConcMarkSweepGC

串行垃圾收集

該算法對年輕代使用標記-復制,對老生代使用標記-清除-壓縮。它在單線程上工作。執行時,它會凍結所有其他線程,直到垃圾收集操作結束。

由于串行垃圾收集的線程凍結性質,它僅適用于非常小的程序垃圾收集。

要使用串行 GC,請使用以下 JVM 參數:

-XX:+UseSerialGC

并行垃圾收集

與串行GC類似,它在年輕代中使用標記復制,在老年代中使用標記清除緊湊。多個并發線程用于標記和復制/壓縮階段。可以使用 -XX:ParallelGCThreads=N 選項配置線程數。

如果主要的目標是通過有效利用現有系統資源來提高吞吐量,則并行垃圾收集器適用于多核計算機。使用這種方法,可以大大縮短 GC 循環時間。

要使用并行 GC,請使用以下 JVM 參數:

-XX:+UseParallelGC

G1垃圾收集

G1(垃圾優先)垃圾收集器在 Java 7 中可用,旨在作為 CMS 收集器的長期替代品。G1 收集器是一個并行、并發、增量壓縮的低暫停垃圾收集器。

這種方法涉及將內存堆分割成多個小區域(通常為 2048 個)。每個區域都被標記為年輕代(進一步分為eden regions或survivor regions)或老年代。這使得 GC 可以避免一次收集整個堆,而是逐步解決問題。這意味著一次僅考慮區域的子集。

G1 持續跟蹤每個區域包含的實時數據量。該信息用于確定包含最多垃圾的區域;所以首先收集它們。這就是為什么它被稱為垃圾優先收集。

不幸的是,就像其他算法一樣,壓縮操作是使用 Stop the World 方法進行的。但根據其設計目標,可以為其設置特定的性能目標。還可以配置暫停持續時間,例如在任何給定的秒內不超過 10 毫秒。垃圾優先 GC 將盡最大努力以高概率實現這一目標(但不確定,由于操作系統級別的線程管理)。

如果你想在 Java 7 或 Java 8 機器上使用,請使用 JVM 參數,如下所示:

-XX:+UseSerialGC

G1 優化選項

  • -XX:G1HeapReginotallow=16m 堆區域的大小。該值是 2 的冪,范圍從 1MB 到 32MB。目標是根據最小 Java 堆大小擁有大約 2048 個區域。
  • -XX:MaxGCPauseMillis=200 設置所需最大暫停時間的目標值。默認值為 200 毫秒。指定的值不適合堆大小。
  • -XX:G1ReservePercent=5 這確定堆中的最小保留量。
  • -XX:G1Cnotallow=75 這是確信度百分比。
  • -XX:GCPauseIntervalMillis=200 這是每個 MMU 的暫停間隔時間片(以毫秒為單位)。

建議

G1配置

-XX:+UseG1GC \
-XX:+UseStringDeduplication \
-XX:+ParallelRefProcEnabled \
-XX:+AlwaysPreTouch \
-XX:+DisableExplicitGC \
-XX:ParallelGCThreads=8 \
-XX:GCTimeRatio=9 \
-XX:MaxGCPauseMillis=25 \
-XX:MaxGCMinorPauseMillis=5 \
-XX:ConcGCThreads=8 \
-XX:InitiatingHeapOccupancyPercent=70 \
-XX:MaxTenuringThreshold=10 \
-XX:SurvivorRatio=6 \
-XX:-UseAdaptiveSizePolicy \
-XX:MaxMetaspaceSize=256M \
-Xmx4G \
-Xms2G \

優化結果

總結

請注意,JVM性能調優是一個復雜的過程,需要結合具體的應用程序特性和需求來進行調優。不同的應用場景可能需要不同的調優策略。在進行JVM性能調優時,應該先進行性能測試和分析,找出性能瓶頸,然后有針對性地進行優化。同時,及時記錄和備份調優前的配置和參數,以便在調優過程中出現問題時能夠恢復到原始狀態。

責任編輯:姜華 來源: 今日頭條
相關推薦

2021-01-27 11:10:49

JVM性能調優

2010-09-27 09:31:42

JVM內存結構

2017-07-21 08:55:13

TomcatJVM容器

2023-10-13 00:09:20

桶排序排序算法

2023-10-08 00:02:07

Java排序算法

2021-01-19 12:00:39

前端監控代碼

2021-04-28 10:13:58

zookeeperZNode核心原理

2010-09-17 14:17:05

JVM內存設置

2023-10-09 00:12:55

歸并排序數據

2024-12-12 09:00:28

2024-12-04 15:49:29

2021-11-21 23:03:38

jvm調優虛擬機

2010-09-26 10:53:00

JVM內存調優設置

2024-04-30 11:11:33

aiohttp模塊編程

2017-10-13 15:16:38

Java服務GC參數

2019-02-19 10:25:28

JVM性能工具

2021-01-12 09:03:17

MySQL復制半同步

2024-03-14 09:00:00

2024-07-01 00:00:04

ViteUMD瀏覽器

2023-04-24 14:54:09

JVM性能調優
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人免费视频一区 | 久久久国产精品视频 | 久草热线| 在线观看国产精品视频 | 久草免费福利 | 天天曰夜夜 | 国产久| 国产精品精品久久久久久 | 在线精品亚洲欧美日韩国产 | 精品成人佐山爱一区二区 | 噜噜噜噜狠狠狠7777视频 | 亚洲一区 中文字幕 | 国产一区二区三区在线免费 | 99r在线 | 在线a视频网站 | 久久久久久久一区二区三区 | 久久久成人免费一区二区 | 一区二区三区视频 | 日韩激情视频一区 | 九色在线观看 | av天天看 | 国产精品久久亚洲 | 日韩欧美一区二区在线播放 | 91视频www.| 婷婷二区 | 欧美在线激情 | 国产美女在线观看 | av在线二区| 武道仙尊动漫在线观看 | 欧美一区二区三 | 九九导航 | 99亚洲精品 | 亚洲欧美一区二区三区视频 | 欧美福利在线 | 日本午夜在线视频 | www日韩欧美 | 国产精品精品视频一区二区三区 | 日韩电影a | 久久精品久久综合 | 日韩精品一区二区三区在线观看 | 国产高清视频在线 |