應用程序內存泄漏問題排查 php
1.文章的由來;java
在平常運維過程當中,會遇到服務器資源居高不下,或者CPU內存暴漲問題而引起的oom致使服務不可用 (大多數程序都是java應用),由此編寫了該文章,爲工做排查問題參考依據和快速定位問題方法;web
2.基礎知識儲備;算法
(1).jvm 配置常見參數:spring
堆參數參數bootstrap
參數 |
描述 |
-Xms | 設置 JVM啓動時堆內存的初始化大小 |
-Xmx | 設置堆內存最大值 |
-Xmn | 設置年輕代的空間大小,剩下的爲年老代的空間大小 |
-XX:PermGen | 設置永久代的內存初始化大小(JDK1.8 開始廢棄永久代) |
-XX:MaxPermGen | 設置永久代的最大值 |
-XX:SurvivorRatio | 設置Eden區和Survivor區的空間比例:Eden/S0 =Eden/S1 默認8 |
-XX:NewRatio | 設置年老代和年輕代的比例大小,默認值是2 |
-XX:+UseSerialGC |
串行,young (年輕區)和Old(老年區)都使用串行,使用複製算法回收,邏輯簡單高效,無現場切換開銷 |
-XX:+UseParallelGC | 並行, young (年輕區)使用 Parallel scavenge 回收算法,會產生多個線程並行回收。經過-XX:ParallelGCThreads=n 參數指定有線程數,默認爲cpu核數,Old(老年區):單線程 |
-XX:+UseParallelOldGC | 並行 和UseParallelGC 同樣,young (年輕區) 和Old(老年區) 垃圾回收都使用多線程收集 |
-XX:+UseConMarkSweepGC |
併發,短暫停頓的併發收集,young區可使用普通的或者parallel垃圾回收算法,由參數 --XX:+UseParNewGC 來控制; Old區 只使用Concurrent Mark Sweep |
-XX:+UseG1GC |
並行的,併發的和增量式短暫停頓的垃圾收集器。不區分young (年輕區) 和Old(老年區)空間。它把堆空間分爲多個大小相等的區域。當進行垃圾收集時,他會優先收集存活對象較少的區域。所以叫"Garbage First」 |
如上表所示,目前主要有串行、並行和併發三種,對於大內存的應用而言,串行的性能過低,所以使用到的主要是並行和併發兩種。並行和併發 GC 的策略經過 UseParallelGC 和 UseConcMarkSweepGC 來指定,還有一些細節的配置參數用來配置策略的執行方式。例如:XX:ParallelGCThreads, XX:CMSInitiatingOccupancyFraction 等。 一般:Young 區對象回收只可選擇並行(耗時間),Old 區選擇併發(耗 CPU)。windows
生產環境服務配置參數服務器
系統版本 |
cpu | mem | jdk 版本 | jvm配置 | 備註 |
CentOS 7.5.1804 (Core) 多線程 |
8C併發 |
8G |
java-1.8.0_121 |
java -Xmx5g -Xms5g -Xmn3g -XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=128m -XX:MaxDirectMemorySize=512m -XX:+UseCompressedClassPointers -XX:CompressedClassSpaceSize=64m -XX:+UseConcMarkSweepGC -XX:ParallelCMSThreads=2 -XX:+CMSClassUnloadingEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80 -verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+PrintCommandLineFlags -XX:+ExplicitGCInvokesConcurrent -Xloggc:/data/log/app-web/gc.log -Dapp.id=app-web -Dapollo.bootstrap.enabled=true -Duser.timezone=GMT+8 -Dapollo.bootstrap.namespaces=application -Deureka.instance.metadata-map.zone=zone-1 -Dservice.name=app-web -Denv=prod -Dprod_meta=http://configserver-prod.com -Druntime.env=prod -jar ./lib/app-web-1.8.10.0.jar |
微服務 springcloud 框架 |
常見組合
GC 調優原則
在調優以前,咱們須要記住下面的原則:
多數的 Java 應用不須要在服務器上進行 GC 優化;
多數致使 GC 問題的 Java 應用,都不是由於咱們參數設置錯誤,而是代碼問題;
在應用上線以前,先考慮將機器的 JVM 參數設置到最優(最適合);
減小建立對象的數量;
減小使用全局變量和大對象;
GC 優化是到最後不得已才採用的手段;
在實際使用中,分析 GC 狀況優化代碼比優化 GC 參數要多得多。
將轉移到老年代的對象數量下降到最小;
減小 GC 的執行時間。
策略 1:將新對象預留在新生代,因爲 Full GC 的成本遠高於 Minor GC,所以儘量將對象分配在新生代是明智的作法,實際項目中根據 GC 日誌分析新生代空間大小分配是否合理,適當經過「-Xmn」命令調節新生代大小,最大限度下降新對象直接進入老年代的狀況。
策略 2:大對象進入老年代,雖然大部分狀況下,將對象分配在新生代是合理的。可是對於大對象這種作法卻值得商榷,大對象若是首次在新生代分配可能會出現空間不足致使不少年齡不夠的小對象被分配的老年代,破壞新生代的對象結構,可能會出現頻繁的 full gc。所以,對於大對象,能夠設置直接進入老年代(固然短命的大對象對於垃圾回收老說簡直就是噩夢)。-XX:PretenureSizeThreshold 能夠設置直接進入老年代的對象大小。
策略 3:合理設置進入老年代對象的年齡,-XX:MaxTenuringThreshold 設置對象進入老年代的年齡大小,減小老年代的內存佔用,下降 full gc 發生的頻率。
策略 4:設置穩定的堆大小,堆大小設置有兩個參數:-Xms 初始化堆大小,-Xmx 最大堆大小。
策略5:注意: 若是知足下面的指標,則通常不須要進行 GC 優化:
MinorGC 執行時間不到50ms;
Minor GC 執行不頻繁,約10秒一次;
Full GC 執行時間不到1s;
Full GC 執行頻率不算頻繁,不低於10分鐘1次。
3.排查技巧戰
1.內存泄漏排查技巧
系統命令
1.登錄探測服務器,首先是 top free df 三連;
jstat 是一個很是強大的 JVM 監控工具,通常用法是:jstat [-options] pid interval
它支持的查看項有:
-class 查看類加載信息
-compile 編譯統計信息
-gc 垃圾回收信息
-gcXXX 各區域 GC 的詳細信息 如 -gcold
3. jstat -gc pid [interval] 查看 java 進程的 GC 狀態;
關注指標: FULL GC 。
4. jstack pid > jstack.log 查詢線程棧 並保存現場;
棧的分析很簡單,看一下線程數是否是過多,多數棧都在幹嗎。
> grep 'java.lang.Thread.State' jstack.log | wc -l
> 464
過濾進程
grep -A 1 'java.lang.Thread.State' jstack.log | grep -v 'java.lang.Thread.State' | sort | uniq -c |sort -n
10 at java.lang.Class.forName0(Native Method)
10 at java.lang.Object.wait(Native Method)
16 at java.lang.ClassLoader.loadClass(ClassLoader.java:404)
44 at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
344 at sun.misc.Unsafe.park(Native Method)
線程狀態好像也無異常,接下來分析堆文件。
內存堆dump
5.使用 jmap -dump:format=b,file=heap.log pid 保存了堆現場,而後重啓了應用服務
堆文件都是一些二進制數據,在命令行查看很是麻煩,Java 爲咱們提供的工具都是可視化的,Linux 服務器上又無法查看,那麼首先要把文件下載到本地。
因爲咱們設置的堆內存爲 4G,因此 dump 出來的堆文件也很大,下載它確實很是費事,不過咱們能夠先對它進行一次壓縮。=
gzip 是個功能很強大的壓縮命令,特別是咱們能夠設置 -1 ~ -9 來指定它的壓縮級別,數據越大壓縮比率越大,耗時也就越長,推薦使用 -6~7, -9 實在是太慢了,且收益不大,有這個壓縮的時間,多出來的文件也下載好了。
MAT 是分析 Java 堆內存的利器,使用它打開咱們的堆文件(將文件後綴改成 .hprof), 它會提示咱們要分析的種類,果斷選擇 memory leak suspect。
windows 系統安裝包以下:
https://www.eclipse.org/downloads/download.php?file=/mat/1.9.1/rcp/MemoryAnalyzer-1.9.1.20190826-win32.win32.x86_64.zip