明白生產環境中的jvm參數

来源:https://www.cnblogs.com/hapjin/archive/2018/09/21/9688289.html
-Advertisement-
Play Games

明白生產環境中的jvm參數 寫代碼的時候,程式寫完了,發到線上去運行,跑一段時間後,程式變慢了,cpu負載高了……一堆問題出來了,所以瞭解一下生產環境的機器上的jvm配置是有必要的。比如說: JDK版本是多少?採用何種垃圾回收器? 程式啟動的時候預設分配堆記憶體空間是多少?隨著程式的運行,程式最多能使 ...


明白生產環境中的jvm參數

寫代碼的時候,程式寫完了,發到線上去運行,跑一段時間後,程式變慢了,cpu負載高了……一堆問題出來了,所以瞭解一下生產環境的機器上的jvm配置是有必要的。比如說:

  • JDK版本是多少?採用何種垃圾回收器?
  • 程式啟動的時候預設分配堆記憶體空間是多少?隨著程式的運行,程式最多能使用多大的記憶體空間?
  • 程式中使用了多少個線程?這些線程又處於何種狀態?

瞭解了這些,會對程式的運行有一個更好的瞭解。本文結合生產實踐,記錄一下我常用的一些操作。

註意:如果沒有特殊說明,下麵所有的參數討論都是基於JDK8 server class machine 而言的

根據官方調優文檔,server類型的機器滿足以下要求:

A class of machine referred to as a server-class machine has been defined as a machine with the following:

    • 2 or more physical processors
    • 2 or more GB of physical memory

我的理解:就是這台機器 有兩個以上的物理處理器,並且 具有2G或2G以上的記憶體,那麼就是 server 類型的機器

可通過這個命令查看機器的物理處理器核數:

cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l

可通過這個命令查看機器的總記憶體大小:

cat /proc/meminfo | grep MemTotal

當寫完一個Spring boot Maven 工程,使用 mvn clean package 打包成可運行的jar文件後,可使用如下命令開始執行:

nohup java -Xloggc:${logging_file_location}gc.log -XX:+PrintGCDetails -jar app.jar --spring.profiles.active=${environment} --logging.file.location=${logging_file_location} --domain=com.xx.xxx.xxxx > /dev/null 2>&1 &
  • -Xloggc: 指定程式運行過程中產生的 GC 日誌輸出到 gc.log 文件中。
  • -XX:+PrintGCDetails 指定 輸出詳細的GC日誌。
  • spring.profiles.active=${environment} 可根據 environment變數來選擇是生產環境還是測試環境。有時生產環境中使用的數據源(比如 Mysql)與測試環境不一樣,這樣就很方便。
  • --logging.file.location指定程式輸出的日誌
  • --domain 這個參數主要用來對程式進行標識。比如,使用 ps aux | grep com.xx.xxx.xxxx 就能方便地找到程式的進程號了。

查看GC收集器

JDK版本號一般很容易知道,java --version就行了。那如何知道運行你的程式的JAVA虛擬機採用何種垃圾回收器呢?

其實可以從gc日誌裡面看出 jvm 使用的何種垃圾收集器。我安裝的JDK8,server 類型的機器,新生代預設使用的是:parallel scavenge,而老年代預設使用:ParOldgen 垃圾收集器。而看JVM調優官方文檔:Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide JDK9 預設是G1收集器

一條新生代GC日誌:

0.791: [GC (Allocation Failure) [PSYoungGen: 64000K->3229K(74240K)] 64000K->3237K(243712K), 0.0040270 secs] [Times: user=
0.04 sys=0.00, real=0.00 secs]

一條老年代GC日誌:

152561.075: [Full GC (Ergonomics) [PSYoungGen: 9303K->0K(68096K)] [ParOldGen: 424954K->389313K(439296K)] 434258K->389313K
(507392K), [Metaspace: 42513K->42513K(1087488K)], 0.0598682 secs] [Times: user=0.39 sys=0.01, real=0.06 secs]

查看JVM堆使用

知道了垃圾回收器,再來看看預設情況下,程式運行時初始堆大小,隨著程式的運行,堆記憶體最終可達到多大?

如果在啟動程式時使用-Xmx 指定了最大堆容量,那堆記憶體最終可達到的值,就是 Xmx設置的值(當然,Xmx不可能設置得比機器的物理記憶體還要大,同時也不要設置得和機器記憶體很接近,畢竟還有留一些記憶體給機器上的其他程式用)

下麵以一臺實際的物理機器,來分析下,程式是如何使用堆記憶體的。這台物理機器的記憶體大小為:16225356KB(約為16GB),物理處理器核數為2。因此符合 sever class machine。對於 server class 機器,預設使用如下參數:

On server-class machines, the following are selected by default:

  • Throughput garbage collector
  • Initial heap size of 1/64 of physical memory up to 1 GB
  • Maximum heap size of 1/4 of physical memory up to 1 GB
  • Server runtime compiler
  • 使用以吞吐量優先的GC 回收器。

  • Initial heap size of 1/64 of physical memory up to 1 GB 這句話,解讀很多。我的理解是:JAVA程式啟動時,預設分配的堆大小為:機器物理記憶體的64分之一,在我的示例中,機器的物理記憶體是16225356KB,因為初始時分配的堆大小為247MB:

    16225356/64/1024
    247

    而使用java -XX:+PrintCommandLineFlags命令:(單位是B)

    -XX:InitialHeapSize=259605696 -XX:MaxHeapSize=4153691136

    可看出初始堆大小為259605696B,259605696/1024/1024=247MB。由此可知:JVM啟動時分配的初始堆大小為物理機器記憶體的64分之一。

    然後我再在一臺記憶體為128GB的機器上:

    cat /proc/meminfo | grep MemTotal
    MemTotal: 131829708 kB

    131829708 / 1024 /1024 =125 (也即:128GB記憶體)

    java -XX:+PrintCommandLineFlags
    -XX:InitialHeapSize=2109275328 -XX:MaxHeapSize=32037767584 -XX:+PrintCommandLineFlags -XX:+UseCompressedOops -XX:+UseParallelGC

    可以看出:-XX:InitialHeapSize=2109275328,也即:2109275328/1024/1024=2GB,也就是說:在物理記憶體為128GB的機器上,JAVA堆的初始分配大小為2GB,是超過1GB的。

  • Maximum heap size of 1/4 of physical memory up to 1 GB,隨著程式的運行,JVM堆記憶體會越來越大,但是一個JAVA進程最大能使用多大的堆記憶體空間呢?答案是 四分之一的物理機器記憶體。

    更具體地,對於一臺物理記憶體為16GB的機器,如果在JAVA程式啟動時 不用 Xms、Xmx 參數指定jvm堆大小,即:這個程式就是使用預設的 java堆大小配置,一開始JAVA堆大小為:16GB/64 ,約為:247MB;然後隨著程式的運行,JAVA堆分配的記憶體會動態增大,動態增大的上限是:物理機器記憶體的四分之一,即約為4GB。

    比如,我查看 程式啟動後 第一次 GC日誌如下:

    0.791: [GC (Allocation Failure) [PSYoungGen: 64000K->3229K(74240K)] 64000K->3237K(243712K), 0.0040270 secs] [Times: user=0.04 sys=0.00, real=0.00 secs]

    GC前該記憶體區域(新生代)大小:62MB,GC後該區域的大小:3MB,該區域的總記憶體大小:72MB。
    而GC前JAVA堆使用量62MB,gc後JAVA堆使用量3.1MB. JAVA堆的總大小:238MB(與247MB很接近)

我來做個猜想:根據第一條gc日誌,JAVA堆總大小是238MB,新生代與老年代的比例是1:2,即:-XX:NewRatio=2,1/3的堆是新生代,2/3的堆是老生代,這樣的話,新生代的堆大小是:238/3=79MB,剛好與發生GC的區域總記憶體72MB接近。
而新生代再進一步細分:分為 Eden區、兩個Survivor區,其中Eden區占新生代堆大小的8/10,兩個Survivor區占新生代堆大小的2/10。即:Eden區的大小為:79*0.8=63MB ,與前面提到的 62MB非常接近。也就是說:在Eden區空間不足以容納新創建的對象的時候,發生了一次 PSYoungGen 垃圾回收操作。而Survivor區的大小為79*0.1=8MB,回收完成後,將剩餘的3.1MB對象 存儲 在其中一個Survivor區了。

當隨著程式運行一段時間後:再看一條GC日誌:

95191.984: [GC (Allocation Failure) [PSYoungGen: 48668K->2932K(73216K)] 507352K->461823K(648192K), 0.0032727 secs] [Times: user=0.04 sys=0.00, real=0.01 secs] 

GC前該記憶體區域(新生代)大小:48668/1024=47MB,GC後該區域大小約為2MB,該區域的總記憶體大小:71MB。GC前JAVA堆記憶體的使用量 507352/1024=495MB,GC後JAVA堆的記憶體使用量461823/1024=450MB,JAVA堆記憶體總大小:648192/1024=633MB

可見,運行一段時間後,JAVA堆記憶體大小從:238MB,動態增加到了:633MB

進程的各種狀態

一般我們會用 ps aux | grep java來查看java進程,知道進程ID號(比如13988)後,可通過:

cat /proc/13998/status | grep Threads
Threads: 78

查看一個JAVA進程下一共啟動了多少個線程。

另外,ps aux 中其中有一列是顯示進程狀態的,那進程狀態有哪些呢?

man ps 找到的進程狀態的解釋:

PROCESS STATE CODES
Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process:
D uninterruptible sleep (usually IO)
R running or runnable (on run queue)
S interruptible sleep (waiting for an event to complete)
T stopped, either by a job control signal or because it is being traced.
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z defunct ("zombie") process, terminated but not reaped by its parent.

   For BSD formats and when the stat keyword is used, additional characters may be displayed:
   <    high-priority (not nice to other users)
   N    low-priority (nice to other users)
   L    has pages locked into memory (for real-time and custom IO)
   s    is a session leader
   l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
   +    is in the foreground process group.

D 代表不可中斷的阻塞,比如說I/O操作。不管是顯示IO,還是隱式IO,訪問本地磁碟的IO操作時,一般會處於D狀態。

In practice, processes typically go into D state ("uninterruptible sleep") when they're blocked on access to a local disk, whether that's explicit I/O (read/write) or implicit (paging).

S代表可中斷的睡眠狀態,比如線程執行下麵的代碼:sleep(500),就處於可中斷的睡眠狀態吧。

    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        logger.info("thread interrupted:{}", e.getCause());
    }

關於S狀態的解釋:(waiting for an event to complete),比如說,線程A在爭搶鎖時,由於這把鎖已經被線程B拿到了,那麼 線程A 就會進入 S 狀態吧,線程A等待著線程B釋放鎖這一事件。

談到線程的狀態,其實有個參數與線程狀態息息相關,那就是CPU負載。處於哪個狀態的線程,才會計入CPU的負載呢?

There are two contributions to the load factor: number of processes/threads on the ready-to-run queue and the number blocked on I/O. The processes blocked on I/O show up in the "D" state in ps and top and also contribute to this number.

Not all processes blocked on I/O are in D state - for a common example, processes blocked on I/O to a network socket or terminal will simply be in the S state, and not count towards load.

可以這樣理解:準備運行的線程( ready-to-run queue)和阻塞在I/O操作上的線程都是計入負載的。

但是阻塞在I/O操作上的線程有兩種狀態,一種是D狀態,另一種是S狀態。其中S狀態的線程是不計入負載的。

參考資料:

關於CPU負載可參考這兩篇文章:cpu load過高問題排查Understanding Linux CPU Load - when should you be worried?

Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide JDK9

JVAVA 8 GC tuning
Hotspot JVM的常用選項
原文:https://www.cnblogs.com/hapjin/p/9688289.html


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

-Advertisement-
Play Games
更多相關文章
  • 你現在是棒球比賽記錄員。 給定一個字元串列表,每個字元串可以是以下四種類型之一:1.整數(一輪的得分):直接表示您在本輪中獲得的積分數。2. "+"(一輪的得分):表示本輪獲得的得分是前兩輪有效 回合得分的總和。3. "D"(一輪的得分):表示本輪獲得的得分是前一輪有效 回合得分的兩倍。4. "C" ...
  • 前面提及的:OSI,TCP-IP,IP地址,埠,協議概念我都清楚,所以我直接跳過前面,來到使用這裡。 ...
  • set集合以{}保存一組可迭代對象,如列表,字元串,set集合本身。集合內的元素若有重覆的,將自動去除重覆元素 顯示結果 a.remove(2) print(a)輸出 {1,3} 小知識點: set集合可以看做是數學上的無序和無重覆元素的集合,因此兩個集合之間可以做數學意義上的交集和並集 ...
  • 話說,Java之路已經走的挺長的,本人也算入門級選手,既已上路,那就走的漂亮些。在路上,我會努力踏實地規範自己,完備自己,讓自己做一個社會主義合格用心好碼農。俗話說,事情要麼不做,要麼做就做得漂亮,Java這件事也是開始做了,只是之前沒有用心,所以呢,從現在做一個好碼農啦。 來,下麵讓我們切入正題, ...
  • 給定 S 和 T 兩個字元串,當它們分別被輸入到空白的文本編輯器後,判斷二者是否相等,並返回結果。 # 代表退格字元。 示例 1: 示例 2: 示例 3: 示例 4: 提示: 根據這一題,掌握數據結構中棧的使用 題目分析: 題目的意思是,在兩個字元串中,對於每一個字元串,如果存在'#'符號,並且它前 ...
  • 我想很多程式員應該記得 GitHub 上有一個 Awesome - XXX 系列的資源整理。awesome-ios 就是 vsouza 發起維護的 iOS 資源列表,內容包括:框架、組件、測試、Apple Store、SDK、XCode、網站、書籍等。Swift 語言寫成的項目會被標記為 ★ ,Ap ...
  • 前言:項目中經常要用到Maven,從來也沒有配置過,直到當人問到Maven是乾什麼的,是怎麼管理項目的?一頭霧水,所以寫了這篇博客,首先附上百度百科的詞條: Maven項目對象模型(POM),可以通過一小段描述信息來管理項目的構建,報告和文檔的軟體項目管理工具。 一、Maven的下載環境變數配置 下 ...
  • 題意 題目鏈接 Sol 如果給出的樹是鏈的話顯然就是LIS 不是鏈的時候直接當鏈做,每個節點維護一個multiset表示計算LIS過程中的單調棧 啟髮式合併即可 時間複雜度:$O(nlog^2n)$ ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...