5、JVM性能優化(內存泄漏、內存溢出、各種JVM命令、定位內存問題)

一、內存溢出、內存泄露

1、內存溢出

程序在申請內存時,沒有足夠的內存空間

  • 棧溢出
    虛擬機棧(線程獨享的棧空間)StackOverflowError
    棧溢出:不斷創建線程,使得棧空間被打滿,OutOfMemoryError
  • 堆溢出
    不斷的創建對象使得堆大小小於要創建的對象大小OutOfMemoryError
  • 直接內存溢出
    分配的本地內存大小大於 JVM 的限制
  • 方法區溢出
    在經常動態生產大量 Class 的應用中

2、內存泄漏

程序在申請內存後,無法釋放已申請的內存,導致可用內存變少

  • 長生命週期對象持有短生命週期對象的引用
    例如,ArrayList 設置爲靜態變量,則容器中的對象在程序結束之前將不能被釋放,從而造成內存泄漏

  • 連接未關閉
    如數據庫連接、網絡連接、I/O等只有在使用完關閉後,GC纔會回收他

  • 變量作用域不合理
    一個變量的作用範圍大於其使用範圍或未能及時的對象置爲null

  • 內部類持有外部類的引用
    Java 的非靜態內部類的這種創建方式,會隱式地持有外部類的引用,而且默認情況下這個引用是強引用,因此,如果內部類的生命週期長於外部類的生命 週期,程序很容易就產生內存泄漏。
    如果內部類的生命週期長於外部類的生命週期,程序很容易就產生內存泄漏(你認爲垃圾回收器會回收掉外部類的實例,但由於內部類持有外部類的引 用,導致垃圾回收器不能正常工作)
    解決方法:你可以在內部類的內部顯示持有一個外部類的軟引用(或弱引用),並通過構造方法的方式傳遞進來,在內部類的使用過程中,先判斷一下外部 類是否被回收;

  • Hash 值改變
    在集合中,如果修改了對象中的那些參與計算哈希值的字段,會導致無法從集合中單獨刪除當前對象,造成內存泄露。

3、二者辨析

  • 內存溢出:實實在在的內存空間不足導致;
  • 內存泄漏:該釋放的對象沒有釋放,多見於自己使用容器保存元素的情況下。 如何避免:
  • 內存溢出:檢查代碼以及設置足夠的空間
  • 內存泄漏:一定是代碼有問題 往往很多情況下,內存溢出往往是內存泄漏造成的。

二、內存溢出定位排查工具MAT

JVM中有兩個參數
-XX:+HeapDumpOnOutOfMemoryError :當內存溢出時導出內存快照
-XX:HeapDumpPath=/usr/local/app/oom :指定導出內存快照的路徑

MAT是一種可視化分析dump日誌的工具。

1、深堆和淺堆

  • 淺堆:是指一個對象所消耗的內存。例如,在 32 位系統中,一個對象引用會佔據 4 個字節,一個 int 類型會佔據 4 個字節,long 型變量 會佔據 8 個字節,每個對象頭需要佔用 8 個字節。
  • 深堆:這個對象被 GC 回收後,可以真實釋放的內存大小,也就是隻能通過對象被直接或間接訪問到的所有對象的集合。

在這裏插入圖片描述
A引用了C、D、E。B引用了E,那麼A的淺堆是A本身,深堆是ACD三個對象的內存大小總和,因爲A被GC回收的時候可以回收ACD三個對象。

使用MAT工具分析的時候可以看到
在這裏插入圖片描述
在這裏插入圖片描述
可以看到當前線程中有對象linkedList淺堆是32byte但是深堆是28M多,這是因爲我們程序中linkedList中存放了很多的對象,並且都是強引用,所以可以根據深度和淺堆來定位下是否有對象強引用了過多的對象導致的內存溢出。

2、incoming 和 outgoing

分別是當前對象被那些對象引用和引用了那些對象,可以幫助我們分析對象引用關係,進而分析是否發生了內存泄漏。

三、JDK提供的JVM操作命令

1、JPS

列出當前機器上運行的虛擬機進程,jps從操作系統的臨時目錄去找

  • -q :僅僅顯示進程
  • -m:輸出主函數傳入的參數. 下的 hello 就是在執行程序時從命令行輸入的參數
  • -l: 輸出應用程序主類完整 package 名稱或 jar 完整名稱.
  • -v: 列出 jvm 參數, -Xms20m -Xmx50m 是啓動程序指定的 jvm 參數

2、jstat

是用於監視虛擬機各種運行狀態信息的命令行工具。它可以顯示本地或者遠程虛擬機進程中的類裝載、內存、垃圾收集、JIT 編譯等運行數據,在沒有 GUI 圖形界面,只提供了純文本控制檯環境的服務器上,它將是運行期定位虛擬機性能問題的首選工具。

假設需要每 250 毫秒查詢一次進程 13616 垃圾收集狀況,一共查詢 10 次,那命令應當是:jstat-gc 13616 250 10
常用參數:
-class (類加載器)
-compiler (JIT)
-gc (GC 堆狀態)
-gccapacity (各區大小)
-gccause (最近一次 GC 統計和原因) -gcnew (新區統計)
-gcnewcapacity (新區大小)
-gcold (老區統計)
-gcoldcapacity (老區大小) -gcpermcapacity (永久區大小)
-gcutil (GC 統計彙總) -printcompilation (HotSpot 編譯統計)

3、Jinfo

查看和修改虛擬機的參數

  • jinfo –sysprops 可以查看由 System.getProperties()取得的參數
  • jinfo –flag 未被顯式指定的參數的系統默認值
  • jinfo –flags(注意 s)顯示虛擬機的參數
  • jinfo –flag +[ 參數 ] 可以增加參數,但是僅限於由 java -XX:+PrintFlagsFinal –version 查詢出來且爲 manageable 的參數
  • jinfo –flag -[參數] 可以去除參數

4、jmap

用於生成堆轉儲快照(一般稱爲 heapdump 或 dump 文件)。jmap 的作用並不僅僅是爲了獲取 dump 文件,它還可以查詢 finalize 執行隊列、Java 堆和永 久代的詳細信息,如空間使用率、當前用的是哪種收集器等。和 jinfo 命令一樣,jmap 有不少功能在 Windows 平臺下都是受限的,除了生成 dump 文件 -dump 選項和用於查看每個類的實例、空間佔用統計的-histo 選項在所有操作系統都提供之外,其餘選項都只能在 Linux/Solaris 下使用。
使用方法: jmap -dump:live,format=b,file=heap.bin <pid>
Sun JDK 提供 jhat(JVM Heap Analysis Tool)命令與 jmap 搭配使用,來分析 jmap 生成的堆轉儲快照。

5、jhat

jhat dump 文件名
後屏幕顯示“Server is ready.”的提示後,用戶在瀏覽器中鍵入 http://localhost:7000/就可以訪問詳情
使用 jhat 可以在服務器上生成堆轉儲文件分析(一般不推薦,畢竟佔用服務器的資源,比如一個文件就有 1 個 G)

6、jstack

(Stack Trace for Java)命令用於生成虛擬機當前時刻的線程快照。線程快照就是當前虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等都是導致線程長時間停頓的常見原因。 在代碼中可以用 java.lang.Thread 類的 getAllStackTraces()方法用於獲取虛擬機中所有線程的 StackTraceElement 對象。使用這個方法可以通過簡單的幾行 代碼就完成 jstack 的大部分功能,在實際項目中不妨調用這個方法做個管理員頁面,可以隨時使用瀏覽器來查看線程堆棧。

四、可視化工具

JMX(Java Management Extensions,即 Java 管理擴展)是一個爲應用程序、設備、系統等植入管理功能的框架。JMX 可以跨越一系列異構操作系統平臺、 系統體系結構和網絡傳輸協議,靈活的開發無縫集成的系統、網絡和服務管理應用。
管理遠程進程需要在遠程程序的啓動參數中增加:
-Djava.rmi.server.hostname=…
-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

1、Jconsole

  • 查看內存數據
    在這裏插入圖片描述
  • 執行GC

在這裏插入圖片描述

  • 線程相關信息
    在這裏插入圖片描述

2、visualvm

  • 查看JVM參數、概述信息
    在這裏插入圖片描述
  • 監視JVM信息
    在這裏插入圖片描述
  • 導出內存快照
    在這裏插入圖片描述
  • 線程相關信息
    在這裏插入圖片描述
  • 分析佔用cpu使用情況
    在這裏插入圖片描述
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章