Java虚拟机|JVM知识点汇总及简述->性能监控与调优
性能監(jiān)控與調(diào)優(yōu)
- 前言
這里學(xué)完整章后選擇一到兩個工具使用熟練,個人推薦Visual VM和Arthas搭配熟練使用
一、概述
1.性能評價/測試指標(biāo)
1.1 停頓時間(響應(yīng)時間)
- 提交請求和返回該請求的響應(yīng)之間使用的時間,一般比較關(guān)注平均響應(yīng)時間常用操作的響應(yīng)時間列表:
- 在垃圾回收環(huán)節(jié)中,暫停時間:執(zhí)行垃圾收集時,程序的工作線程被暫停的時間。
1.2 吞吐量
-
對單位時間內(nèi)完成的工作量(請求)的量度
-
在GC中:運(yùn)行用戶代碼的時間占總運(yùn)行時間的比例(總運(yùn)行時間:程序的運(yùn)行時間+內(nèi)存回收的時間)吞吐量為1-1/(1+n)。
-XX:GCTimeRatio=n
1.3 并發(fā)數(shù)
同—時刻,對服務(wù)器有實際交互的請求數(shù)
1.4 內(nèi)存占用
Java堆區(qū)所占的內(nèi)存大小
1.5 相互間的關(guān)系
這里主要討論停頓時間、吞吐量、并發(fā)數(shù)之間的關(guān)系,當(dāng)吞吐量越高,并發(fā)數(shù)也就也高,而停頓時間就越短
二、JVM監(jiān)控及診斷工具-命令行
1.概述
使用數(shù)據(jù)說明問題,使用知識分析問題,使用工具處理問題。無監(jiān)控、不調(diào)優(yōu)!
2.jps:查看正在運(yùn)行的Java進(jìn)程
- 說明
Java process status,顯示指定系統(tǒng)內(nèi)所有的HotSpot虛擬機(jī)進(jìn)程(查看虛擬機(jī)進(jìn)程信息),可用于查詢正在運(yùn)行的虛擬機(jī)進(jìn)程。
說明:對于本地虛擬機(jī)進(jìn)程來說,進(jìn)程的本地虛擬機(jī)ID與操作系統(tǒng)的進(jìn)程ID是一致的,是唯一的。
- options參數(shù):
參數(shù)說明:
-q:僅僅顯示LVMID (local virtual machine id),即本地虛擬機(jī)唯一id。不顯示主類的名稱等
-l:輸出應(yīng)用程序主類的全類名或如果進(jìn)程執(zhí)行的是jar包,則輸出jar完整路徑
-m:輸出虛擬機(jī)進(jìn)程啟動時傳遞給主類main()的參數(shù)
-v:列出虛擬機(jī)進(jìn)程啟動時的JVM參數(shù)。比如:-Xms20m -Xmx50m是啟動程序指定的jvm參數(shù)。
- 注意:
如果某Java進(jìn)程關(guān)閉了默認(rèn)開啟的UsePerfData參數(shù)(即使用參數(shù)-XX:-UsePerfData),那么jps命令(以及下面介紹的jstat)將無法探知該Java進(jìn)程。
3.jstat:查看JVM的統(tǒng)計信息
- 說明
-
JVM Statistics Monitoring Tool:用于監(jiān)視虛擬機(jī)各種運(yùn)行狀態(tài)信息的命令行工具。它可以顯示本地或者遠(yuǎn)程虛擬機(jī)進(jìn)程中的類裝載、內(nèi)存、垃圾收集、IT編譯等運(yùn)行數(shù)據(jù)。
-
在沒有GUI圖形界面,只提供了純文本控制臺環(huán)境的服務(wù)器上,它將是運(yùn)行期定位虛擬機(jī)性能問題的首選工具。常用于檢測垃圾回收問題以及內(nèi)存泄漏問題。
3.1 option參數(shù)
- 類裝載相關(guān)的
-class:顯示ClassLoader的相關(guān)信息:類的裝載、卸載數(shù)量、總空間、類裝載所消耗的時間等
- 垃圾相關(guān)的
- JIT相關(guān)的
3.2 其他參數(shù)
- interval參數(shù)
用于指定輸出統(tǒng)計數(shù)據(jù)的周期,單位為毫秒。即:查詢間隔
-
count參數(shù)
用于指定查詢的總次數(shù),跟在interval參數(shù)后面配合使用
-
-t參數(shù)
可以在輸出信息加上一個TimeStamp列,來顯示程序自打開運(yùn)行的時間,單位:秒
可以根據(jù)-t參數(shù)來判斷是否要出現(xiàn)OOM:比較Java進(jìn)程的啟動時間以及總GC時間(GCT列),或者兩次測量的間隔時間以及總GC時間的增量,來得出 GC時間占運(yùn)行時間的比例。如果該比例超過20%,則說明目前堆的壓力較大;如果該比例超過90%,則悅明堆里幾乎沒有可用空間,隨時都可能拋出 OOM異常。
- -h參數(shù)
可以在周期性數(shù)據(jù)輸出時,輸出多少行數(shù)據(jù)后輸出一個表頭信息
3.3 如何通過jstat判斷內(nèi)存泄露
分別為兩步:
4.jinfo:實時查看和修改JVM配置參數(shù)
- 說明
Configuration Info for Java,在很多情況下,Java應(yīng)用程序不會指定所有的Java虛擬機(jī)參數(shù)。而此時,開發(fā)人員可能不知道某一個具體的Java虛擬機(jī)參數(shù)的默認(rèn)值。在這種情況下,可能需要通過查找文檔獲取某個參數(shù)的默認(rèn)值。這個查找過程可能是非常艱難的。但有了jinfo工具,開發(fā)人員可以很方便地找到Java虛擬機(jī)參數(shù)的當(dāng)前值。
4.1 option參數(shù)
基本使用語法:jinfo [ options ] pid
注意:標(biāo)記為manageable的參數(shù)非常有限
4.2 拓展參數(shù)
-
java -XX:+PrintFlagsInitial
查看所有JVM參數(shù)啟動的初始值
-
java -xx:+PrintFlagsFinal
查看所有JVM參數(shù)的最終值
-
java -XX:+ PrintCommandLineFlags
查看那些已經(jīng)被用戶或者JVM設(shè)置過的詳細(xì)的XX參數(shù)的名稱和值
5.jmap:導(dǎo)出內(nèi)存映像文件&內(nèi)存使用情況
- 說明:
JVM Memory Map:作用一方面是獲取dump文件(堆轉(zhuǎn)儲快照文件,二進(jìn)制文件),它還可以獲取目標(biāo)Java進(jìn)程的內(nèi)存相關(guān)信息,包括Java堆各區(qū)域的使用情況、堆中對象的統(tǒng)計信息、類加載信息等。
5.1 option參數(shù)
基本語法:
- jmap [option] <pid>
- jmap [option] <executable> <core>
- jmap [option] [server_id@] <remote server IP or hostname>
5.2 兩種用法詳解
-
手動方式:
jmap -dump:live, format=b,file=d:\4.hprof pid
說明:live參數(shù)表示只打印內(nèi)存的存活對象(往往出現(xiàn)OOM的時候就是太多存貨對象回收不了導(dǎo)致的,沒有該參數(shù)就表示打印全部對象),format參數(shù)標(biāo)識打印的文件格式可以被監(jiān)控工具識別,file就是指定文件生成位置,文件名后綴為 .hprof ,pid為進(jìn)程號
-
自動方式:
-XX:+HeapDumpOnoutOfMemoryError:在程序發(fā)生OOM時,導(dǎo)出應(yīng)用程序的當(dāng)前堆快照。
-XX:HeapDumpPath:可以指定堆快照的保存位置。
說明:當(dāng)程序發(fā)生OOM退出系統(tǒng)時,一些瞬時信息都隨著程序的終止而消失,而重現(xiàn)OOM問題往往比較困難或者耗時。此時若能在OOM時,自動導(dǎo)出dump文件就顯得非常迫切。
說明:這兩個參數(shù)都是對于內(nèi)存某一個時刻進(jìn)行的時間點信息,無法做到持續(xù)監(jiān)控
- -jmap -heap pid
- -jmap -histo pid
5.3 小結(jié)
由于jmap將訪問堆中的所有對象,為了保證在此過程中不被應(yīng)用線程干擾,jmap需要借助安全點機(jī)制,讓所有線程停留在不改變堆中數(shù)據(jù)的狀態(tài)。與前面講的jstat則不同,垃圾回收器會主動將jstat所需要的摘要數(shù)據(jù)保存至固定位置之中,而jstat只需直接讀取即可。
- 缺點:
- 由jmap導(dǎo)出的堆快照必定是安全點位置的。這可能導(dǎo)致基于該堆快照的分析結(jié)果存在偏差。
- 加入生命周期只在兩個安全點之間有效,jmap記錄的時候是檢測不到的
- 如果某個線程長時間無法跑到安全點,jmap將一直等下去。
6.jhat:JDK自帶堆分析工具
- 概述
JVM Heap Analysis Tool,jhat內(nèi)置了一個微型的HTTP/HTML服務(wù)器,生成dump文件的分析結(jié)果后,用戶可以在瀏覽器中查看分析結(jié)果(分析虛擬機(jī)轉(zhuǎn)儲快照信息)。使用了jhat命令,就啟動了一個http服務(wù),端口是7000,即http://localhost:7000/,就可以在瀏覽器里分析。
- 注意
jhat命令在JDK9、JDK10中已經(jīng)被刪除,官方建議用VisualVM代替。所以這里就不做過多介紹
7.jstack:打印JVM中的線程快照
- 概述
JVM Stack Trance打印當(dāng)前進(jìn)程的所有線程
- 作用
生成線程快照的作用:可用于定位線程出現(xiàn)長時間停頓的原因,如線程間死鎖、死循環(huán)、請求外部資源導(dǎo)致的長時間等待等問題。這些都是導(dǎo)致線程長時間停頓的常見原因。當(dāng)線程出現(xiàn)停頓時,就可以用jstack顯示各個線程調(diào)用的堆棧情況。
- 快照中需要注意的地方
- 死鎖,Deadlock(重點關(guān)注)
- 等待資源,waiting on condition(重點關(guān)注)
- 等待獲取監(jiān)視器,waiting on monitor entry(重點關(guān)注)阻塞,Blocked(重點關(guān)注)
- 執(zhí)行中,Runnable
- 暫停,Suspended
7.1 option參數(shù)
- -F:當(dāng)正常輸出的請求不被響應(yīng)時,強(qiáng)制輸出線程堆棧
- -l:除堆棧外,顯示關(guān)于鎖的附加信息
- -m:如果調(diào)用到本地方法的話,可以顯示C/C++的堆棧
- -h:幫助操作
8.jcmd:多功能命令行
- 概述
它是一個多功能的工具,可以用來實現(xiàn)前面除了jstat之外所有命令的功能。比如:用它來導(dǎo)出堆、內(nèi)存使用、查看Java進(jìn)程、導(dǎo)出線程信息、執(zhí)行GC、JVM運(yùn)行時間等。官方推薦使用jcmd代替jmap
8.1 基本語法
- jcmd -l:列出所有的JVM進(jìn)程
- jcmd pid help:針對指定的進(jìn)程,列出支持的所有命令
- jcmd pid具體命令團(tuán):顯示指定進(jìn)程的指令命令的數(shù)據(jù)
三、JVM監(jiān)控及診斷工具-GUI
1.工具概述
- 使用命令行工具的弊端
- 工具分類
-
JDK自帶的工具:jConsole、Visual VM、JMC(Java mission control)
-
第三方工具:MAT(Eclipse)、JProfiler、Arthas、Btrace
2.jConsole
2.1 概述
-
從JDK5開始,在JDK中自帶的java監(jiān)控和管理控制臺。
-
用于對VM中內(nèi)存、線程和類等的監(jiān)控,是一個基于JMX(java management extensions)的GUI性能監(jiān)控工具
- 位置:
在JDK目錄下的bin目錄可找到
3.Visual VM
3.1 概述
- Visual VM是一個功能強(qiáng)大的多合一故障診斷和性能監(jiān)控的可視化工具。
- 它集成了多個JDK命令行工具,使用Visual M可用于顯示虛擬機(jī)進(jìn)程及進(jìn)程的配置和環(huán)境信息(jps,jinfo),監(jiān)視應(yīng)用程序的CPU、GC、堆、方法區(qū)及線程的信息(jstat、jstack)等,代替JConsole。
- 在JDK 6 Update 7以后,Visual VM便作為DK的一部分發(fā)布(VisualVM在JDK/bin目錄下),是完全免費(fèi)的
- Visual VM也可以作為獨(dú)立的軟件安裝
- 安裝方式
-
在JDK的bin目錄下,如果沒有則自行下載
-
可在idea中下載啟動Visual 的插件,記得要配置.exe文件和JDK的home目錄
-
插件安裝可在官網(wǎng)或客戶端中下載(強(qiáng)烈推薦Visual GC這個插件)
3.2 主要功能
生成/讀取堆內(nèi)存快照
查看JVM參數(shù)和系統(tǒng)屬性
查看運(yùn)行中的虛擬機(jī)進(jìn)程
生成/讀取線程快照
程序資源的實時監(jiān)控
其他功能
JMX代理連接、遠(yuǎn)程環(huán)境監(jiān)控、CPU分析和內(nèi)存分析
4.MAT
4.1 概述
MAT(Memory Analyzer Tool)工具是一款功能強(qiáng)大的Java堆內(nèi)存分析器。**主要用于dump文件的分析可以用于查找內(nèi)存泄漏以及查看內(nèi)存消耗情況。**MAT是基于Eclipse開發(fā)的,不僅可以單獨(dú)使用,還可以作為插件的形式嵌入在Eclipse中使用。是免費(fèi)軟件
4.2 dump文件信息
- 內(nèi)容
MAT可以分析heap dump文件。在進(jìn)行內(nèi)存分析時,只要獲得了反映當(dāng)前設(shè)備內(nèi)存映像的hprof文件,通過MAT打開就可以直觀地看到當(dāng)前的內(nèi)存信息。一般說來,這些內(nèi)存信息包含:
- 所有的對象信息,包括對象實例、成員變量、存儲于棧中的基本類型值和存儲于堆中的其他對象的
引用值。 - 所有的類信息,包括classloader、類名稱、父類、靜態(tài)變量等. GCRoot到所有的這些對象的引用路徑
- 線程信息,包括線程的調(diào)用棧及此線程的線程局部變量(TLS)
- 優(yōu)點
能夠快速為開發(fā)人員生成內(nèi)存泄漏報表,方便定位問題和分析問題
- 導(dǎo)出dump文件方式
- 可以在visual vm里面生成
- 在第5節(jié)imap參數(shù)中,直接用imap參數(shù)導(dǎo)出
- 有兩個參數(shù)可以導(dǎo)出dump文件-XX:+HeapDumpOnoutOfMemoryError:在程序發(fā)生OOM時,導(dǎo)出應(yīng)用程序的當(dāng)前堆快照。-XX:HeapDumpPath:可以指定堆快照的保存位置。
- 當(dāng)然也可以直接用MAT生成dump文件,前提要知道進(jìn)程號
4.3 分析MAT中的dump文件過程
- histogram
展示了各個類的實例數(shù)目以及這些實例的Shallowheap 或Retainedheap的總和
- thread overview
查看系統(tǒng)中的Java線程、查看局部變量的信息
-
獲得對象相互引用的關(guān)系
with outgoing references:查看這個對象引用了誰
with incoming references:查看誰引用了這個對象
?
4.4 深堆和淺堆
- 淺堆(Shallow Heap)
淺堆是指一個對象所消耗的內(nèi)存。在32位系統(tǒng)中,一個對象引用會占據(jù)4個字節(jié),一個int類型會占據(jù)4個字節(jié),long型變量會占據(jù)8個字節(jié),每個對象頭需要占用8個字節(jié)。根據(jù)堆快照格式不同,對象的大小可能會向8字節(jié)進(jìn)行對齊。以String為例: 2個int值共占8字節(jié),對象引用占用4字節(jié),對象頭8字節(jié),合計20字節(jié),向8字節(jié)對齊,故占24字節(jié)。(jdk7中)。
注意:與value值的多少是無關(guān)的
- 保留集(Retained Set)
對象A的保留集指當(dāng)對象A被垃圾回收后,可以被釋放的所有的對象集合(包括對象A本身),即對象A的保留集可以被認(rèn)為是只能通過對象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對象的內(nèi)存占用
- 考慮JJJ同學(xué):15個 webpage,每個對應(yīng)152個字節(jié) 15* 152= 2280字節(jié),即為elementData的實際大小
- 能被7整除,且能被3整除,以及能被7整除,且能被5整除的數(shù)值有:0,21,42,63,84,35,70 共7個數(shù)。7*152 = 1064字節(jié),2280 -1064 +72 = 1288字節(jié)
- 72個字節(jié)組成為:15個elementData的元素*4字節(jié) =60字節(jié),60+8個對象頭的字節(jié)數(shù)+4字節(jié)=72字節(jié)
4.5 支配樹
- 概述
MAT提供了一個稱為支配樹(Dominator Tree)的對象圖。支配樹體現(xiàn)了對象實例間的支配關(guān)系。**在對象引用圖中,所有指向?qū)ο驜的路徑都經(jīng)過對象A,則認(rèn)為對象A支配對象B。如果對象A是離對象B最近的一個支配對象,則認(rèn)為對象A為對象B的直接支配者。**支配樹是基于社象間的引用圖所建立的,它有以下基本性質(zhì):
- 對象A的子樹(所有被對象A支配的對象集合)表示對象A的保留集(retained set),即深堆。
- 如果對象A支配對象B,那么對象A的直接支配者也支配對象B。
- 支配樹的邊與對象引用圖的邊不直接對應(yīng)。
- 圖示
四、再談內(nèi)存泄露
1.內(nèi)存泄露的理解與分類
- 概述
可達(dá)性分析算法來判斷對象是否是不再使用的對象,本質(zhì)都是判斷一個對象是否還被引用。那么對于這種情況下,由于代碼的實現(xiàn)不同就會出現(xiàn)很多種內(nèi)存泄漏問題(讓JVM誤以為此對象還在引用中,無法回收,造成內(nèi)存泄漏)。
1.1 內(nèi)存泄漏與內(nèi)存溢出的關(guān)系
內(nèi)存泄漏(Memory Leak):
申請了內(nèi)存用完了不釋放,比如一共有1024M 的內(nèi)存,分配了512M的內(nèi)存一直不回收,那么可以用的內(nèi)存只有521M 了,仿佛泄露掉了一部分。
內(nèi)存溢出(Out Of Memory):
申請內(nèi)存時,沒有足夠的內(nèi)存可以使用。
可見,內(nèi)存泄漏和內(nèi)存溢出的關(guān)系:內(nèi)存泄漏的增多,最終會導(dǎo)致內(nèi)存溢出。
1.2 內(nèi)存泄漏的分類
- 經(jīng)常發(fā)生:發(fā)生內(nèi)存泄露的代碼會被多次執(zhí)行,每次執(zhí)行,泄露一塊內(nèi)存;
- 偶然發(fā)生:在某些特定情況下才會發(fā)生;
- 一次性:發(fā)生內(nèi)存泄露的方法只會執(zhí)行一次;
- 隱式泄漏:一直占著內(nèi)存不釋放,直到執(zhí)行結(jié)束;嚴(yán)格的說這個不算內(nèi)存泄漏,因為最終釋放掉了,但是如果執(zhí)行時間特別長,也可能會導(dǎo)致內(nèi)存耗盡。
2.Java中內(nèi)存泄漏的8種情況
2.1 靜態(tài)集合類
靜態(tài)集合類,如HashMap、LinkedList等等。如果這些容器為靜態(tài)的,那么它們的生命周期與JVM程序一致,則容器中的對象在程序結(jié)束之前將不能被釋放,從而造成內(nèi)存泄漏。簡單而言,長生命周期的對象持有短生命周期對象的引用,盡管短生命周期的對象不再使用,但是因為長生命周期對象持有它的引用而導(dǎo)致不能被回收。
public class test01(){static List list = new ArrayList();public void oomTest(){Object o = new Object();//局部變量list.add(o);} }2.2 單例模式
單例模式,和靜態(tài)集合導(dǎo)致內(nèi)存泄露的原因類似,因為單例的靜態(tài)特性,它的生命周期和JVM 的生命周期一樣長,所以如果單例對象如果持有外部對象的引用,那么這個外部對象也不會被回收,那么就會造成內(nèi)存泄漏。
2.3 內(nèi)部類持有外部類
內(nèi)部類持有外部類,如果一個外部類的實例對象的方法返回了一個內(nèi)部類的實例對象。
這個內(nèi)部類對象被長期引用了,即使那個外部類實例對象不再被使用,但由于內(nèi)部類持有外部類的實例對象,這個外部類對象將不會被垃圾回收,這也會造成內(nèi)存泄漏。
2.4 各種連接,數(shù)據(jù)庫連接、網(wǎng)絡(luò)連接和IO連接等
在對數(shù)據(jù)庫進(jìn)行操作的過程中,首先需要建立與數(shù)據(jù)庫的連接,當(dāng)不再使用時,需要調(diào)用close方法來釋放與數(shù)據(jù)庫的連接。只有連接被關(guān)閉后,垃圾回收器才會回收對應(yīng)的對象。
否則,如果在訪問數(shù)據(jù)庫的過程中,對Connection、Statement或ResultSet不顯性地關(guān)閉,將會造成大量的對象無法被回收,從而引起內(nèi)存泄漏。
2.5 變量不合理作用域
變量不合理的作用域。一般而言,一個變量的定義的作用范圍大于其使用范圍,很有可能會造成內(nèi)存泄漏。另一方面,如果沒有及時地把對象設(shè)置為null,很有可能導(dǎo)致內(nèi)存泄漏的發(fā)生。
public class UsingRandom {private String msg;public void receiveMsg(){readFromNet();//從網(wǎng)絡(luò)中接受數(shù)據(jù)保存到msg中saveDB();//把msg保存到數(shù)據(jù)庫中} } //如上面這個偽代碼,通過readFromNet方法把接受的消息保存在變量 //msg中,然后調(diào)用saveDB方法把msg的內(nèi)容保存到數(shù)據(jù)庫中, //此時msg已經(jīng)就沒用了,由于msg的生命周期與對象的生命周期相同, //此時msg還不能回收,因此造成了內(nèi)存泄漏。 //實際上這個msg變量可以放在receiveMsg方法內(nèi)部,當(dāng)方法使用完, //那么msg的生命周期也就結(jié)束,此時就可以回收了。還有一種方法, //在使用完msg后,把msg設(shè)置為null, //這樣垃圾回收器也會回收msg的內(nèi)存空間。2.6 改變哈希值
改變哈希值,當(dāng)一個對象被存儲進(jìn)HashSet集合中以后,就不能修改這個對象中的那些參與計算哈希值的字段了。
否則,對象修改后的哈希值與最初存儲進(jìn)HashSet集合中時的哈希值就不同了,在這種情況下,即使在contains方法使用該對象的當(dāng)前引用作為的參數(shù)去HashSet集合中檢索對象,也將返回找不到對象的結(jié)果,這也會導(dǎo)致無法從HashSet集合中單獨(dú)刪除當(dāng)前對象,造成內(nèi)存泄漏。
這也是String為什么被設(shè)置成了不可變類型,我們可以放心地把String存入 HashSet,或者把String 當(dāng)做HashMap的key 值;
2.7 緩存泄漏
內(nèi)存泄漏的另一個常見來源是緩存,一旦你把對象引用放入到緩存中,他就很容易遺忘。比如:之前項目在一次上線的時候,應(yīng)用啟動奇慢直到夯死,就是因為代碼中會加載一個表中的數(shù)據(jù)到緩存(內(nèi)存)中,測試環(huán)境只有幾百條數(shù)據(jù),但是生產(chǎn)環(huán)境有幾百萬的數(shù)據(jù)。
對于這個問題,可以使用WeakHashMap(軟引用)代表緩存,此種Map的特點是,當(dāng)除了自身有對key的引用外,此key沒有其他引用那么此map會自動丟棄此值。
2.8 監(jiān)聽器和回調(diào)
內(nèi)存泄漏另一個常見來源是監(jiān)聽器和其他回調(diào),如果客戶端在你實現(xiàn)的API中注冊回調(diào),卻沒有顯示的取消,那么就會積聚。
需要確保回調(diào)立即被當(dāng)作垃圾回收的最佳方法是只保存它的弱引用,例如將他們保存成為weakHashMap中的鍵。
3.內(nèi)存泄漏案例分析
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已經(jīng)減過了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();} }- 出現(xiàn)的問題
匿名線程始終不能被GC
- 解決辦法
五、OQL語言查詢對象信息
- 概述
MAT支持一種類似于SQL的查詢語言O(shè)QL (Object Query Language)。OQL使用類SQL語法,可以在堆中進(jìn)行對象的查找和篩選。
1.SELECT子句
在MAT中,Select子句的格式與SQL基本一致,用于指定要顯示的列。Select子句中可以使用“*”,查看結(jié)果對象的引用實例(相當(dāng)于outgoing references)。
-
使用“OB3ECTS”關(guān)鍵字,可以將返回結(jié)果集中的項以對象的形式顯示。
SELECT objects v.elementData FROM java.util.Vector v
SELECT OBECTS s.value FROM java.lang.string s
-
在Select子句中,使用“AS RETAINED SET”關(guān)鍵字可以得到所得對象的保留集。
SELECT AS RETAINED SET * FROM com.atguigu.mat. Student
-
“DISTINCT”關(guān)鍵字用于在結(jié)果集中去除重復(fù)對象。
SELECT DISTINCT OBECTS classof(s)FROM java.lang.String s
2.FROM子句
-
From子句用于指定查詢范圍,它可以指定類名、正則表達(dá)式或者對象地址。
SELECT * FROM java.lang.String s
-
下例使用正則表達(dá)式,限定搜索范圍,輸出所有com.atguigu包下所有類的實例
SELECT FROM "com.atguigul…”
-
也可以直接使用類的地址進(jìn)行搜索。使用類的地址的好處是可以區(qū)分被不同ClassLoader加載的同一種類型。
select * from 0x37a0b4d
3.WHERE子句
where子句用于指定oQL的查詢條件。oQL查詢將只返回滿足where子句指定條件的對象。Where子句的格式與傳統(tǒng)SQL極為相似。
- 下例返回長度大于10的char數(shù)組。
SELECT * FROM char[] s WHERE s.@length>10 - 下例返回包含“java”子字符串的所有字符串,使用“LIKE”操作符,“LIKE”操作符的操作參數(shù)為正則表達(dá)式。
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運(yùn)算。下例返回數(shù)組長度大于15,并且深堆大于1000字節(jié)的所有Vector對象。
SELECT * FROM java.util.Vector v WHERE v.elementData.@length>15 AN Dv.@retainedHeapsize>1000
4.內(nèi)置對象與方法
0QL中可以訪問堆內(nèi)對象的屬性,也可以訪問堆內(nèi)代理對象的屬性。訪問堆內(nèi)對象的屬性時,格式如下:
[ <alias>. ] <field> . <field>. <field>其中alias為對象名稱。
-
訪問java.io.File對象的path屬性,并進(jìn)一步訪問path的value屬性:
SELECT toString(f.path.value)FROM java.io.File f
-
下例顯示了String對象的內(nèi)容、objectid和objectAddress。
SELECT s.toString(), s.@objectId, s.@objectAddress FROMjava.lang.String s -
下例顯示java.util.Vector內(nèi)部數(shù)組的長度。
SELECT v.elementData.@length FROM java.util.Vector v -
下例顯示了所有的java.util.Vector對象及其子類型
select * from INSTANCEOF java.util.Vector
六、JProfiler
1.基本概述
想要用一款集成在idea的分析工具,或想要比mat工具更加全面,JProfiler由此誕生,是一款Java應(yīng)用性能診斷工具,功能強(qiáng)大,但注意是收費(fèi)的
1.1 特點
- 使用方便、界面操作友好―(簡單且強(qiáng)大)
- 對被分析的應(yīng)用影響小(提供模板)
- CPU, Thread ,Memory分析功能尤其強(qiáng)大
- 支持對jdbc ,nosql,jsp, servlet, socket等進(jìn)行分析
- 支持多種模式(離線,在線)的分析
- 支持監(jiān)控本地、遠(yuǎn)程的JVM
- 跨平臺,擁有多種操作系統(tǒng)的安裝版本
1.2 主要功能
2.具體使用
2.1 數(shù)據(jù)采集方式
- Instrumentation(重構(gòu)模式):這是JProfiler全功能模式。在class加載之前,JProfier把相關(guān)功能代碼寫入到需要分析的class的bytecode中,對正在運(yùn)行的jvm有一定影響。
- 優(yōu)點:功能強(qiáng)大。在此設(shè)置中,調(diào)用堆棧信息是準(zhǔn)確的。
- 缺點:若要分析的class較多,則對應(yīng)用的性能影響較大,CPU開銷可能很高(取決于Filter的控制)。因此使用此模式一般配合Filter使用,只對特定的類或包進(jìn)行分析。
-
Full sampling(抽樣模式):類似于樣本統(tǒng)計,每隔一定時間(5ms )將每個線程棧中方法棧中的信息統(tǒng)計出來。
- 優(yōu)點:對[PU的開銷非常低,對應(yīng)用影響小(即使你不配置任何Filter)
- 缺點:一些數(shù)據(jù)/特性不能提供(例如:方法的調(diào)用次數(shù)、執(zhí)行時間)
2.2 各種重要功能
這里這些功能演示及案例分析就不扣字寫了,大家想要了解請去尚硅谷看JVM視頻(343~349)
- 遙感監(jiān)測(Telemetries)
- 內(nèi)存視圖(Live Memory)
- 堆遍歷(heap walker)
- cpu視圖(cpu views)
- 線程視圖(threads)
- 監(jiān)視器&鎖(Monitors&locks)
七、Arthas
1.前奏
1.1 JProfiler與JvisualVM的缺點
這兩款工具有個缺點,都必須在服務(wù)端項目進(jìn)程中配置相關(guān)的監(jiān)控參數(shù)。然后工具通過遠(yuǎn)程連接到項目進(jìn)程,獲取相關(guān)的數(shù)據(jù)。這樣就會帶來一些不便,比如線上環(huán)境的網(wǎng)絡(luò)是隔離的,本地的監(jiān)控工具根本連不上線上環(huán)境。并且類似于Jprofiler這樣的商業(yè)工具,是需要付費(fèi)的。
1.2 概述
Arthas(阿爾薩斯)是Alibaba開源的Java診斷工具,在線排查問題,無需重啟;動態(tài)跟蹤Java代碼;實時監(jiān)控JVM狀態(tài)。Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同時提供豐富的Tab自動補(bǔ)全功能,進(jìn)一步方便進(jìn)行問題的定位和診斷。借鑒并基于很多優(yōu)秀的軟件組合而成的一個工具
- 注意:
由于是開源的項目且是國人開發(fā),這里就不做過多介紹,大家可移步至官網(wǎng)查看中文文檔https://arthas.aliyun.com/doc/,里面有十分詳細(xì)的說明
八、JMC(Java Mission Control)
1.概述
是oracle公司自己的工具,在JDK的bin目錄下找到j(luò)mc.exe可執(zhí)行文件
九、其他調(diào)優(yōu)工具
- Tprofiler:是由阿里開源的一款尋找錯誤熱點代碼的工具
- Btrace:簡潔明了,大意是一個Java平臺的安全的動態(tài)追蹤工具。可以用來動態(tài)地追蹤一個運(yùn)行的Java程序。BTrace動態(tài)調(diào)整目標(biāo)應(yīng)用程序的類以注入跟蹤代碼(“字節(jié)碼跟蹤”)。
- YourKit
- JProbe
- Spring Insight
總結(jié)
以上是生活随笔為你收集整理的Java虚拟机|JVM知识点汇总及简述->性能监控与调优的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mac 字体微软雅黑字体_在Micros
- 下一篇: Java学习记录