日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java虚拟机|JVM知识点汇总及简述->性能监控与调优

發布時間:2023/12/20 java 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java虚拟机|JVM知识点汇总及简述->性能监控与调优 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

性能監控與調優

  • 前言

這里學完整章后選擇一到兩個工具使用熟練,個人推薦Visual VM和Arthas搭配熟練使用

一、概述

1.性能評價/測試指標

1.1 停頓時間(響應時間)

  • 提交請求和返回該請求的響應之間使用的時間,一般比較關注平均響應時間常用操作的響應時間列表:

  • 在垃圾回收環節中,暫停時間:執行垃圾收集時,程序的工作線程被暫停的時間。

1.2 吞吐量

  • 對單位時間內完成的工作量(請求)的量度

  • 在GC中:運行用戶代碼的時間占總運行時間的比例(總運行時間:程序的運行時間+內存回收的時間)吞吐量為1-1/(1+n)。

    -XX:GCTimeRatio=n

1.3 并發數

同—時刻,對服務器有實際交互的請求數

1.4 內存占用

Java堆區所占的內存大小

1.5 相互間的關系

這里主要討論停頓時間、吞吐量、并發數之間的關系,當吞吐量越高,并發數也就也高,而停頓時間就越短

二、JVM監控及診斷工具-命令行

1.概述

使用數據說明問題,使用知識分析問題,使用工具處理問題。無監控、不調優!

2.jps:查看正在運行的Java進程

  • 說明

Java process status,顯示指定系統內所有的HotSpot虛擬機進程(查看虛擬機進程信息),可用于查詢正在運行的虛擬機進程。
說明:對于本地虛擬機進程來說,進程的本地虛擬機ID與操作系統的進程ID是一致的,是唯一的。

  • options參數:

參數說明:

-q:僅僅顯示LVMID (local virtual machine id),即本地虛擬機唯一id。不顯示主類的名稱等
-l:輸出應用程序主類的全類名或如果進程執行的是jar包,則輸出jar完整路徑
-m:輸出虛擬機進程啟動時傳遞給主類main()的參數
-v:列出虛擬機進程啟動時的JVM參數。比如:-Xms20m -Xmx50m是啟動程序指定的jvm參數。

  • 注意:

如果某Java進程關閉了默認開啟的UsePerfData參數(即使用參數-XX:-UsePerfData),那么jps命令(以及下面介紹的jstat)將無法探知該Java進程。

3.jstat:查看JVM的統計信息

  • 說明
  • JVM Statistics Monitoring Tool:用于監視虛擬機各種運行狀態信息的命令行工具。它可以顯示本地或者遠程虛擬機進程中的類裝載、內存、垃圾收集、IT編譯等運行數據。

  • 在沒有GUI圖形界面,只提供了純文本控制臺環境的服務器上,它將是運行期定位虛擬機性能問題的首選工具。常用于檢測垃圾回收問題以及內存泄漏問題。

3.1 option參數

  • 類裝載相關的

-class:顯示ClassLoader的相關信息:類的裝載、卸載數量、總空間、類裝載所消耗的時間等

  • 垃圾相關的
  • -gc:顯示與GC相關的堆信息。包括Eden區、兩個Survivor區、老年代、永久代等的容量、己用空間、GC時間合計等信息。
  • -gccapacity:顯示內容與-gc基本相同,但輸出主要關注Java堆各個區域使用到的最大、最小空間。
  • -gcutil:顯示內容與-gc基本相同,但輸出主要關注已使用空間占總空間的百分比。
  • -gccause:與-gcutil功能一樣,但是會額外輸出導致最后一次或當前正在發生的GC產生的原因。
  • -gcnew:顯示新生代GC狀況
  • -gcnewcapacity:顯示內容與-gcnew基本相同,輸出主要關注使用到的最大、最小空間
  • -geold:顯示老年代GC狀況
  • -gcoldcapacity:顯示內容與-gcold基本相同,輸出主要關注使用到的最大、最小空間
  • -gcpermcapacity:顯示永久代使用到的最大、最小空間。
    • JIT相關的
  • -compiler:顯示JIT編譯器編譯過的方法、耗時等信息-
  • printcompilation:輸出已經被JIT編譯的方法
  • 3.2 其他參數

    • interval參數

    用于指定輸出統計數據的周期,單位為毫秒。即:查詢間隔

    • count參數

      用于指定查詢的總次數,跟在interval參數后面配合使用

    • -t參數

    可以在輸出信息加上一個TimeStamp列,來顯示程序自打開運行的時間,單位:秒

    可以根據-t參數來判斷是否要出現OOM:比較Java進程的啟動時間以及總GC時間(GCT列),或者兩次測量的間隔時間以及總GC時間的增量,來得出 GC時間占運行時間的比例。如果該比例超過20%,則說明目前堆的壓力較大;如果該比例超過90%,則悅明堆里幾乎沒有可用空間,隨時都可能拋出 OOM異常。

    • -h參數

    可以在周期性數據輸出時,輸出多少行數據后輸出一個表頭信息

    3.3 如何通過jstat判斷內存泄露

    分別為兩步:

  • 在長時間運行的 Java程序中,我們可以運行jstat命令連續獲取多行性能數據,并取這幾行數據中OU列(即已占用的老年代內存)的最小值。
  • 每隔一段較長的時間重復一次上述操作,來獲得多組OU最小值。如果這些值呈上漲趨勢,則說明該Java程序的老年代內存己使用量在不斷上漲,這意味著無法回收的對象在不斷增加,因此很有可能存在內存泄漏。
  • 4.jinfo:實時查看和修改JVM配置參數

    • 說明

    Configuration Info for Java,在很多情況下,Java應用程序不會指定所有的Java虛擬機參數。而此時,開發人員可能不知道某一個具體的Java虛擬機參數的默認值。在這種情況下,可能需要通過查找文檔獲取某個參數的默認值。這個查找過程可能是非常艱難的。但有了jinfo工具,開發人員可以很方便地找到Java虛擬機參數的當前值。

    4.1 option參數

    基本使用語法:jinfo [ options ] pid

    注意:標記為manageable的參數非常有限

    4.2 拓展參數

    • java -XX:+PrintFlagsInitial

      查看所有JVM參數啟動的初始值

    • java -xx:+PrintFlagsFinal

      查看所有JVM參數的最終值

    • java -XX:+ PrintCommandLineFlags

      查看那些已經被用戶或者JVM設置過的詳細的XX參數的名稱和值

    5.jmap:導出內存映像文件&內存使用情況

    • 說明:

    JVM Memory Map:作用一方面是獲取dump文件(堆轉儲快照文件,二進制文件),它還可以獲取目標Java進程的內存相關信息,包括Java堆各區域的使用情況、堆中對象的統計信息、類加載信息等。

    5.1 option參數

    基本語法:

    • jmap [option] <pid>
    • jmap [option] <executable> <core>
    • jmap [option] [server_id@] <remote server IP or hostname>

    5.2 兩種用法詳解

  • 導出內存映像文件(dump)
    • 手動方式:

      jmap -dump:live, format=b,file=d:\4.hprof pid

      說明:live參數表示只打印內存的存活對象(往往出現OOM的時候就是太多存貨對象回收不了導致的,沒有該參數就表示打印全部對象),format參數標識打印的文件格式可以被監控工具識別,file就是指定文件生成位置,文件名后綴為 .hprof ,pid為進程號

    • 自動方式:

      -XX:+HeapDumpOnoutOfMemoryError:在程序發生OOM時,導出應用程序的當前堆快照。

      -XX:HeapDumpPath:可以指定堆快照的保存位置。

      說明:當程序發生OOM退出系統時,一些瞬時信息都隨著程序的終止而消失,而重現OOM問題往往比較困難或者耗時。此時若能在OOM時,自動導出dump文件就顯得非常迫切。

  • 顯示內存使用情況
  • 說明:這兩個參數都是對于內存某一個時刻進行的時間點信息,無法做到持續監控

    • -jmap -heap pid
    • -jmap -histo pid

    5.3 小結

    由于jmap將訪問堆中的所有對象,為了保證在此過程中不被應用線程干擾,jmap需要借助安全點機制,讓所有線程停留在不改變堆中數據的狀態。與前面講的jstat則不同,垃圾回收器會主動將jstat所需要的摘要數據保存至固定位置之中,而jstat只需直接讀取即可。

    • 缺點:
    • 由jmap導出的堆快照必定是安全點位置的。這可能導致基于該堆快照的分析結果存在偏差。
    • 加入生命周期只在兩個安全點之間有效,jmap記錄的時候是檢測不到的
    • 如果某個線程長時間無法跑到安全點,jmap將一直等下去。

    6.jhat:JDK自帶堆分析工具

    • 概述

    JVM Heap Analysis Tool,jhat內置了一個微型的HTTP/HTML服務器,生成dump文件的分析結果后,用戶可以在瀏覽器中查看分析結果(分析虛擬機轉儲快照信息)。使用了jhat命令,就啟動了一個http服務,端口是7000,即http://localhost:7000/,就可以在瀏覽器里分析。

    • 注意

    jhat命令在JDK9、JDK10中已經被刪除,官方建議用VisualVM代替。所以這里就不做過多介紹

    7.jstack:打印JVM中的線程快照

    • 概述

    JVM Stack Trance打印當前進程的所有線程

    • 作用

    生成線程快照的作用:可用于定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等問題。這些都是導致線程長時間停頓的常見原因。當線程出現停頓時,就可以用jstack顯示各個線程調用的堆棧情況。

    • 快照中需要注意的地方
    • 死鎖,Deadlock(重點關注)
    • 等待資源,waiting on condition(重點關注)
    • 等待獲取監視器,waiting on monitor entry(重點關注)阻塞,Blocked(重點關注)
    • 執行中,Runnable
    • 暫停,Suspended

    7.1 option參數

    • -F:當正常輸出的請求不被響應時,強制輸出線程堆棧
    • -l:除堆棧外,顯示關于鎖的附加信息
    • -m:如果調用到本地方法的話,可以顯示C/C++的堆棧
    • -h:幫助操作

    8.jcmd:多功能命令行

    • 概述

    它是一個多功能的工具,可以用來實現前面除了jstat之外所有命令的功能。比如:用它來導出堆、內存使用、查看Java進程、導出線程信息、執行GC、JVM運行時間等。官方推薦使用jcmd代替jmap

    8.1 基本語法

    • jcmd -l:列出所有的JVM進程
    • jcmd pid help:針對指定的進程,列出支持的所有命令
    • jcmd pid具體命令團:顯示指定進程的指令命令的數據

    三、JVM監控及診斷工具-GUI

    1.工具概述

    • 使用命令行工具的弊端
  • 無法獲取方法級別的分析數據,如方法間的調用關系、各方法的調用次數和調用時間等(這對定位應用性能瓶頸至關重要)。
  • 要求用戶登錄到目標Java應用所在的宿主機上,使用起來不是很方便。
  • 分析數據通過終端輸出,結果展示不夠直觀。
    • 工具分類
    • JDK自帶的工具:jConsole、Visual VM、JMC(Java mission control)

    • 第三方工具:MAT(Eclipse)、JProfiler、Arthas、Btrace

    2.jConsole

    2.1 概述

    • 從JDK5開始,在JDK中自帶的java監控和管理控制臺。

    • 用于對VM中內存、線程和類等的監控,是一個基于JMX(java management extensions)的GUI性能監控工具

    • 位置:

    在JDK目錄下的bin目錄可找到

    3.Visual VM

    3.1 概述

    • Visual VM是一個功能強大的多合一故障診斷和性能監控的可視化工具。
    • 它集成了多個JDK命令行工具,使用Visual M可用于顯示虛擬機進程及進程的配置和環境信息(jps,jinfo),監視應用程序的CPU、GC、堆、方法區及線程的信息(jstat、jstack)等,代替JConsole。
    • 在JDK 6 Update 7以后,Visual VM便作為DK的一部分發布(VisualVM在JDK/bin目錄下),是完全免費的
    • Visual VM也可以作為獨立的軟件安裝
    • 安裝方式
    • 在JDK的bin目錄下,如果沒有則自行下載

    • 可在idea中下載啟動Visual 的插件,記得要配置.exe文件和JDK的home目錄

    • 插件安裝可在官網或客戶端中下載(強烈推薦Visual GC這個插件)

    3.2 主要功能

  • 生成/讀取堆內存快照

  • 查看JVM參數和系統屬性

  • 查看運行中的虛擬機進程

  • 生成/讀取線程快照

  • 程序資源的實時監控

  • 其他功能

    JMX代理連接、遠程環境監控、CPU分析和內存分析

  • 4.MAT

    4.1 概述

    MAT(Memory Analyzer Tool)工具是一款功能強大的Java堆內存分析器。**主要用于dump文件的分析可以用于查找內存泄漏以及查看內存消耗情況。**MAT是基于Eclipse開發的,不僅可以單獨使用,還可以作為插件的形式嵌入在Eclipse中使用。是免費軟件

    4.2 dump文件信息

    • 內容

    MAT可以分析heap dump文件。在進行內存分析時,只要獲得了反映當前設備內存映像的hprof文件,通過MAT打開就可以直觀地看到當前的內存信息。一般說來,這些內存信息包含:

    • 所有的對象信息,包括對象實例、成員變量、存儲于棧中的基本類型值和存儲于堆中的其他對象的
      引用值。
    • 所有的類信息,包括classloader、類名稱、父類、靜態變量等. GCRoot到所有的這些對象的引用路徑
    • 線程信息,包括線程的調用棧及此線程的線程局部變量(TLS)
    • 優點

    能夠快速為開發人員生成內存泄漏報表,方便定位問題和分析問題

    • 導出dump文件方式
    • 可以在visual vm里面生成
    • 在第5節imap參數中,直接用imap參數導出
    • 有兩個參數可以導出dump文件-XX:+HeapDumpOnoutOfMemoryError:在程序發生OOM時,導出應用程序的當前堆快照。-XX:HeapDumpPath:可以指定堆快照的保存位置。
    • 當然也可以直接用MAT生成dump文件,前提要知道進程號

    4.3 分析MAT中的dump文件過程

    • histogram

    展示了各個類的實例數目以及這些實例的Shallowheap 或Retainedheap的總和

    • thread overview

    查看系統中的Java線程、查看局部變量的信息

    • 獲得對象相互引用的關系

      with outgoing references:查看這個對象引用了誰

      with incoming references:查看誰引用了這個對象

    ?

    4.4 深堆和淺堆

    • 淺堆(Shallow Heap)

    淺堆是指一個對象所消耗的內存。在32位系統中,一個對象引用會占據4個字節,一個int類型會占據4個字節,long型變量會占據8個字節,每個對象頭需要占用8個字節。根據堆快照格式不同,對象的大小可能會向8字節進行對齊。以String為例: 2個int值共占8字節,對象引用占用4字節,對象頭8字節,合計20字節,向8字節對齊,故占24字節。(jdk7中)。

    注意:與value值的多少是無關的

    • 保留集(Retained Set)

    對象A的保留集指當對象A被垃圾回收后,可以被釋放的所有的對象集合(包括對象A本身),即對象A的保留集可以被認為是只能通過對象A被直接或間接訪問到的所有對象的集合。通俗地說,就是指僅被對象A所持有的對象的集合

    • 深堆(Retained Heap)

    就是自己的淺堆大小加上保留集的大小就是深堆的大小

    • 對象實際大小

    對象的實際大小就是指:淺堆大小+自己能夠引用的全部對象大小

    • 案例分析

    代碼:

    public class StudentTrace {static List<WebPage> webpages = new ArrayList<>();public static void createWebPages() {for (int i = 0; i < 100; i++) {WebPage wp = new WebPage();wp.setUrl( "http://www." + Integer.toString(i) + ".com" );wp.setContent( Integer.toString(i));webpages.add(wp);}}public static void main(String[] args) {createWebPages();Student s3 = new Student(3,"LLL");Student s5 = new Student(5,"HHH");Student s7 = new Student(7,"JJJ");for (int i = 0; i < webpages.size(); i++) {if (i % s3.getId() == 0)s3.visit(webpages.get(i));if (i %s5.getId( ) == 0)s5.visit(webpages.get(i));if (i %s7.getId( ) == 0)s7.visit(webpages.get(i));}webpages.clear();System.gc();} }class Student{private int id;private String name;private List<WebPage> history = new ArrayList<>();public Student(int id, String name) {this.id = id;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<WebPage> getHistory() {return history;}public void setHistory(List<WebPage> history) {this.history = history;}public void visit(WebPage wp){if(wp != null){history.add(wp);}} }class WebPage{private String url;private String content;public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getContent() {return content;}public void setContent(String content) {this.content = content;} }

    分析7號JJJ對象的內存占用

    • 考慮JJJ同學:15個 webpage,每個對應152個字節 15* 152= 2280字節,即為elementData的實際大小
    • 能被7整除,且能被3整除,以及能被7整除,且能被5整除的數值有:0,21,42,63,84,35,70 共7個數。7*152 = 1064字節,2280 -1064 +72 = 1288字節
    • 72個字節組成為:15個elementData的元素*4字節 =60字節,60+8個對象頭的字節數+4字節=72字節

    4.5 支配樹

    • 概述

    MAT提供了一個稱為支配樹(Dominator Tree)的對象圖。支配樹體現了對象實例間的支配關系。**在對象引用圖中,所有指向對象B的路徑都經過對象A,則認為對象A支配對象B。如果對象A是離對象B最近的一個支配對象,則認為對象A為對象B的直接支配者。**支配樹是基于社象間的引用圖所建立的,它有以下基本性質:

    • 對象A的子樹(所有被對象A支配的對象集合)表示對象A的保留集(retained set),即深堆。
    • 如果對象A支配對象B,那么對象A的直接支配者也支配對象B。
    • 支配樹的邊與對象引用圖的邊不直接對應。
    • 圖示

    四、再談內存泄露

    1.內存泄露的理解與分類

    • 概述

    可達性分析算法來判斷對象是否是不再使用的對象,本質都是判斷一個對象是否還被引用。那么對于這種情況下,由于代碼的實現不同就會出現很多種內存泄漏問題(讓JVM誤以為此對象還在引用中,無法回收,造成內存泄漏)。

    1.1 內存泄漏與內存溢出的關系

  • 內存泄漏(Memory Leak):
    申請了內存用完了不釋放,比如一共有1024M 的內存,分配了512M的內存一直不回收,那么可以用的內存只有521M 了,仿佛泄露掉了一部分。

  • 內存溢出(Out Of Memory):

    申請內存時,沒有足夠的內存可以使用。

  • 可見,內存泄漏和內存溢出的關系:內存泄漏的增多,最終會導致內存溢出。

    1.2 內存泄漏的分類

    • 經常發生:發生內存泄露的代碼會被多次執行,每次執行,泄露一塊內存;
    • 偶然發生:在某些特定情況下才會發生;
    • 一次性:發生內存泄露的方法只會執行一次;
    • 隱式泄漏:一直占著內存不釋放,直到執行結束;嚴格的說這個不算內存泄漏,因為最終釋放掉了,但是如果執行時間特別長,也可能會導致內存耗盡。

    2.Java中內存泄漏的8種情況

    2.1 靜態集合類

    靜態集合類,如HashMap、LinkedList等等。如果這些容器為靜態的,那么它們的生命周期與JVM程序一致,則容器中的對象在程序結束之前將不能被釋放,從而造成內存泄漏。簡單而言,長生命周期的對象持有短生命周期對象的引用,盡管短生命周期的對象不再使用,但是因為長生命周期對象持有它的引用而導致不能被回收。

    public class test01(){static List list = new ArrayList();public void oomTest(){Object o = new Object();//局部變量list.add(o);} }

    2.2 單例模式

    單例模式,和靜態集合導致內存泄露的原因類似,因為單例的靜態特性,它的生命周期和JVM 的生命周期一樣長,所以如果單例對象如果持有外部對象的引用,那么這個外部對象也不會被回收,那么就會造成內存泄漏。

    2.3 內部類持有外部類

    內部類持有外部類,如果一個外部類的實例對象的方法返回了一個內部類的實例對象。
    這個內部類對象被長期引用了,即使那個外部類實例對象不再被使用,但由于內部類持有外部類的實例對象,這個外部類對象將不會被垃圾回收,這也會造成內存泄漏。

    2.4 各種連接,數據庫連接、網絡連接和IO連接等

    在對數據庫進行操作的過程中,首先需要建立與數據庫的連接,當不再使用時,需要調用close方法來釋放與數據庫的連接。只有連接被關閉后,垃圾回收器才會回收對應的對象。
    否則,如果在訪問數據庫的過程中,對Connection、Statement或ResultSet不顯性地關閉,將會造成大量的對象無法被回收,從而引起內存泄漏。

    public static void main( String[] args) {try {Connection conn = null;Class.forName( "com.mysql.jdbc.Driver" );conn = DriverManager.getConnection( "ur1", "","");Statement stmt = conn.createStatement();Resultset rs = stmt.executeQuery("....");}catch (Exception e) {//異常日志}finally {//1.關閉結果集Statement//2.關閉聲明的對象ResultSet//3.關閉連接Connection } }

    2.5 變量不合理作用域

    變量不合理的作用域。一般而言,一個變量的定義的作用范圍大于其使用范圍,很有可能會造成內存泄漏。另一方面,如果沒有及時地把對象設置為null,很有可能導致內存泄漏的發生。

    public class UsingRandom {private String msg;public void receiveMsg(){readFromNet();//從網絡中接受數據保存到msg中saveDB();//把msg保存到數據庫中} } //如上面這個偽代碼,通過readFromNet方法把接受的消息保存在變量 //msg中,然后調用saveDB方法把msg的內容保存到數據庫中, //此時msg已經就沒用了,由于msg的生命周期與對象的生命周期相同, //此時msg還不能回收,因此造成了內存泄漏。 //實際上這個msg變量可以放在receiveMsg方法內部,當方法使用完, //那么msg的生命周期也就結束,此時就可以回收了。還有一種方法, //在使用完msg后,把msg設置為null, //這樣垃圾回收器也會回收msg的內存空間。

    2.6 改變哈希值

    改變哈希值,當一個對象被存儲進HashSet集合中以后,就不能修改這個對象中的那些參與計算哈希值的字段了。
    否則,對象修改后的哈希值與最初存儲進HashSet集合中時的哈希值就不同了,在這種情況下,即使在contains方法使用該對象的當前引用作為的參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會導致無法從HashSet集合中單獨刪除當前對象,造成內存泄漏。
    這也是String為什么被設置成了不可變類型,我們可以放心地把String存入 HashSet,或者把String 當做HashMap的key 值;

    public class test02 {public static void main(String[ ] args) {HashSet set = new HashSet( );Person p1 = new Person( id: 1001,name: "AA" );Person p2 = new Person( id: 1002,name: "BB");set.add(p1);set.add(p2);p1.name = "CC";//此操作就是導致對象刪除不掉的原因set.remove(p1);System.out.println(set);set.add(new Person(1001,"CC" ));System.out.println(set);//輸出三個對象set.add(new Person(1001,"AA"));System.out.printLn(set);//輸出四個對象,解析如下圖} }class Person{private int id;private String name;public Person(int id, String name) {this.id = id;this.name = name;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (!(o instanceof Person)) return false;Person person = (Person) o;return getId() == person.getId() && Objects.equals(getName(), person.getName());}@Overridepublic int hashCode() {return Objects.hash(id, name);}@Overridepublic String toString() {return "Person{" +"id=" + id +", name='" + name + '\'' +'}';}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;} }

    2.7 緩存泄漏

    內存泄漏的另一個常見來源是緩存,一旦你把對象引用放入到緩存中,他就很容易遺忘。比如:之前項目在一次上線的時候,應用啟動奇慢直到夯死,就是因為代碼中會加載一個表中的數據到緩存(內存)中,測試環境只有幾百條數據,但是生產環境有幾百萬的數據。
    對于這個問題,可以使用WeakHashMap(軟引用)代表緩存,此種Map的特點是,當除了自身有對key的引用外,此key沒有其他引用那么此map會自動丟棄此值。

    public class MapTest {static Map wMap = new WeakHashMap();static Map map = new HashMap();public static void main(String[] args) {init();testweakHashMap( );testHashMap();}public static void init() {String s1 = new String( original: "obejct1");String s2 = new String( original: "obejct2");String s3 = new String( original: "obejct3");String s4 = new strEing( original: "obejct4");wMap.put(s1, "cacheObject1");wMap.put(s2,"cacheObject2");map.put(ref3,"cacheObject3" );map.put(ref4,"cacheObject4" );System.out.println("string引用s1,s2,s3,s4消失");}public static void testweakHashMap() {System.out.print1n( "weakHashMap GC之前");for (Object o : wMap.entrySet()){System.out.println(o);} try {System.gc();TimeUnit.SECONDS.sleep( timeout: 5);}catch (InterruptedException e) {e.printStackTrace();}System.out.println( "weakHashMap GC之后");for (object o : wMap. entrySet()) {System.out.println(o);}}public static void testHashMap() {System.out.print1n( "HashMap GC之前");for (Object o : Map.entrySet()){System.out.println(o);} try {System.gc();TimeUnit.SECONDS.sleep( timeout: 5);}catch (InterruptedException e) {e.printStackTrace();}System.out.println( "HashMap GC之后");for (object o : Map. entrySet()) {System.out.println(o);}} }

    2.8 監聽器和回調

    內存泄漏另一個常見來源是監聽器和其他回調,如果客戶端在你實現的API中注冊回調,卻沒有顯示的取消,那么就會積聚。
    需要確保回調立即被當作垃圾回收的最佳方法是只保存它的弱引用,例如將他們保存成為weakHashMap中的鍵。

    3.內存泄漏案例分析

    3.1 案例一

    public class MyStack {private Object[] elements;private int size = 0;private static final int DEFAULT_INITIAL_CAPACITY = 16;public MyStack() {elements = new Object[DEFAULT_INITIAL_CAPACITY];}//壓棧操作public void push(Object e) {ensureCapacity();elements[size++] = e;}//這個出棧操作是錯誤的,我們只是把想要刪除的指針引用向下面移了,被刪除的對象還占著空間/*public Object pop(){if (size == 0)throw new EmptyStackException();return elements[ --size];}*///這樣就可以了,讓被刪除的對象的值置為nullpublic Object pop(){if(size == 0){throw new EmptyStackException();}Object o = elements[--size];elements[size] = null; //size已經減過了return o;}private void ensureCapacity() {if (elements.length == size)elements = Arrays.copyOf(elements,2 * size + 1);}}

    3.2 案例二

    public class TestActivity extends Activity{private static final Object key = new Object();@Overrideprotected void onCreate( Bundle savedInstanceState) {super.onCreate( savedInstanceState) ;setContentview(R.layout.activity_main);new Thread(){//匿名線程public void run() {synchronized (key) {try {key.wait();}catch (InterruptedException e) {e. printstackTrace();}}}}.start();} }
    • 出現的問題

    匿名線程始終不能被GC

    • 解決辦法
  • 使用線程時,一定要確保線程在周期性對象(如Activity)銷毀時能正常結束,如能正常結束,但是Activity銷毀后還需執行一段時間,也可能造成泄露,此時可采用weakReference方法來解決,另外在使用Handler的時候,如存在Delay操作,也可以采用weakReference;
  • 使用Handler + HandlerThread時,記住在周期性對象銷毀時調用looper.quit()方法;
  • 五、OQL語言查詢對象信息

    • 概述

    MAT支持一種類似于SQL的查詢語言OQL (Object Query Language)。OQL使用類SQL語法,可以在堆中進行對象的查找和篩選。

    1.SELECT子句

    在MAT中,Select子句的格式與SQL基本一致,用于指定要顯示的列。Select子句中可以使用“*”,查看結果對象的引用實例(相當于outgoing references)。

    • 使用“OB3ECTS”關鍵字,可以將返回結果集中的項以對象的形式顯示。

      SELECT objects v.elementData FROM java.util.Vector v

      SELECT OBECTS s.value FROM java.lang.string s

    • 在Select子句中,使用“AS RETAINED SET”關鍵字可以得到所得對象的保留集。

      SELECT AS RETAINED SET * FROM com.atguigu.mat. Student

    • “DISTINCT”關鍵字用于在結果集中去除重復對象。
      SELECT DISTINCT OBECTS classof(s)FROM java.lang.String s

    2.FROM子句

    • From子句用于指定查詢范圍,它可以指定類名、正則表達式或者對象地址。

      SELECT * FROM java.lang.String s

    • 下例使用正則表達式,限定搜索范圍,輸出所有com.atguigu包下所有類的實例

      SELECT FROM "com.atguigul…”

    • 也可以直接使用類的地址進行搜索。使用類的地址的好處是可以區分被不同ClassLoader加載的同一種類型。
      select * from 0x37a0b4d

    3.WHERE子句

    where子句用于指定oQL的查詢條件。oQL查詢將只返回滿足where子句指定條件的對象。Where子句的格式與傳統SQL極為相似。

    • 下例返回長度大于10的char數組。
      SELECT * FROM char[] s WHERE s.@length>10
    • 下例返回包含“java”子字符串的所有字符串,使用“LIKE”操作符,“LIKE”操作符的操作參數為正則表達式。
      SELECT * FROM java.lang.String s WHERE toString(s)LIKE “.java.”
    • 下例返回所有value域不為null的字符串,使用“=”操作符。
      SELECT * FROM java.lang.String s where s.value !=null
    • where子句支持多個條件的AND、OR運算。下例返回數組長度大于15,并且深堆大于1000字節的所有Vector對象。
      SELECT * FROM java.util.Vector v WHERE v.elementData.@length>15 AN Dv.@retainedHeapsize>1000

    4.內置對象與方法

    0QL中可以訪問堆內對象的屬性,也可以訪問堆內代理對象的屬性。訪問堆內對象的屬性時,格式如下:
    [ <alias>. ] <field> . <field>. <field>其中alias為對象名稱。

    • 訪問java.io.File對象的path屬性,并進一步訪問path的value屬性:

      SELECT toString(f.path.value)FROM java.io.File f

    • 下例顯示了String對象的內容、objectid和objectAddress。
      SELECT s.toString(), s.@objectId, s.@objectAddress FROMjava.lang.String s

    • 下例顯示java.util.Vector內部數組的長度。
      SELECT v.elementData.@length FROM java.util.Vector v

    • 下例顯示了所有的java.util.Vector對象及其子類型
      select * from INSTANCEOF java.util.Vector

    六、JProfiler

    1.基本概述

    想要用一款集成在idea的分析工具,或想要比mat工具更加全面,JProfiler由此誕生,是一款Java應用性能診斷工具,功能強大,但注意是收費的

    1.1 特點

    • 使用方便、界面操作友好―(簡單且強大)
    • 對被分析的應用影響小(提供模板)
    • CPU, Thread ,Memory分析功能尤其強大
    • 支持對jdbc ,nosql,jsp, servlet, socket等進行分析
    • 支持多種模式(離線,在線)的分析
    • 支持監控本地、遠程的JVM
    • 跨平臺,擁有多種操作系統的安裝版本

    1.2 主要功能

  • 方法調用:對方法調用的分析可以幫助您了解應用程序正在做什么,并找到提高其性能的方法
  • 內存分配:通過分析堆上對象、引用鏈和垃圾收集能幫您修復內存泄漏問題,優化內存使用
  • 線程和鎖:JProfiler提供多種針對線程和鎖的分析視圖助您發現多線程問題
  • 高級子系統:許多性能問題都發生在更高的語義級別上。例如,對于JDBC調用,您可能希望找出;執行最慢的SQL語句。JProfiler支持對這些子系統進行集成分析
  • 2.具體使用

    2.1 數據采集方式

    • Instrumentation(重構模式):這是JProfiler全功能模式。在class加載之前,JProfier把相關功能代碼寫入到需要分析的class的bytecode中,對正在運行的jvm有一定影響。
    • 優點:功能強大。在此設置中,調用堆棧信息是準確的。
    • 缺點:若要分析的class較多,則對應用的性能影響較大,CPU開銷可能很高(取決于Filter的控制)。因此使用此模式一般配合Filter使用,只對特定的類或包進行分析。
    • Full sampling(抽樣模式):類似于樣本統計,每隔一定時間(5ms )將每個線程棧中方法棧中的信息統計出來。

      • 優點:對[PU的開銷非常低,對應用影響小(即使你不配置任何Filter)
      • 缺點:一些數據/特性不能提供(例如:方法的調用次數、執行時間)

    2.2 各種重要功能

    這里這些功能演示及案例分析就不扣字寫了,大家想要了解請去尚硅谷看JVM視頻(343~349)

    • 遙感監測(Telemetries)
    • 內存視圖(Live Memory)
    • 堆遍歷(heap walker)
    • cpu視圖(cpu views)
    • 線程視圖(threads)
    • 監視器&鎖(Monitors&locks)

    七、Arthas

    1.前奏

    1.1 JProfiler與JvisualVM的缺點

    這兩款工具有個缺點,都必須在服務端項目進程中配置相關的監控參數。然后工具通過遠程連接到項目進程,獲取相關的數據。這樣就會帶來一些不便,比如線上環境的網絡是隔離的,本地的監控工具根本連不上線上環境。并且類似于Jprofiler這樣的商業工具,是需要付費的。

    1.2 概述

    Arthas(阿爾薩斯)是Alibaba開源的Java診斷工具,在線排查問題,無需重啟;動態跟蹤Java代碼;實時監控JVM狀態。Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同時提供豐富的Tab自動補全功能,進一步方便進行問題的定位和診斷。借鑒并基于很多優秀的軟件組合而成的一個工具

    • 注意:

    由于是開源的項目且是國人開發,這里就不做過多介紹,大家可移步至官網查看中文文檔https://arthas.aliyun.com/doc/,里面有十分詳細的說明

    八、JMC(Java Mission Control)

    1.概述

    是oracle公司自己的工具,在JDK的bin目錄下找到jmc.exe可執行文件

    九、其他調優工具

    • Tprofiler:是由阿里開源的一款尋找錯誤熱點代碼的工具
    • Btrace:簡潔明了,大意是一個Java平臺的安全的動態追蹤工具。可以用來動態地追蹤一個運行的Java程序。BTrace動態調整目標應用程序的類以注入跟蹤代碼(“字節碼跟蹤”)。
    • YourKit
    • JProbe
    • Spring Insight

    總結

    以上是生活随笔為你收集整理的Java虚拟机|JVM知识点汇总及简述->性能监控与调优的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。