一文详解java中对JVM的深度解析、调优工具、垃圾回收
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
jvm監(jiān)控分析工具一般分為兩類,一種是jdk自帶的工具,一種是第三方的分析工具。jdk自帶工具一般在jdk bin目錄下面,以exe的形式直接點(diǎn)擊就可以使用,其中包含分析工具已經(jīng)很強(qiáng)大,幾乎涉及了方方面面,但是我們最常使用的只有兩款:jconsole.exe和jvisualvm.exe;第三方的分析工具有很多,各自的側(cè)重點(diǎn)不同,比較有代表性的:MAT(Memory Analyzer Tool)、GChisto等。
jconsole
Jconsole(Java Monitoring and Management Console)是從java5開(kāi)始,在JDK中自帶的java監(jiān)控和管理控制臺(tái),用于對(duì)JVM中內(nèi)存,線程和類等的監(jiān)控,是一個(gè)基于JMX(java management extensions)的GUI性能監(jiān)測(cè)工具。jconsole使用jvm的擴(kuò)展機(jī)制獲取并展示虛擬機(jī)中運(yùn)行的應(yīng)用程序的性能和資源消耗等信息。
直接在jdk/bin目錄下點(diǎn)擊jconsole.exe即可啟動(dòng),界面如下:
在彈出的框中可以選擇本機(jī)的監(jiān)控本機(jī)的java應(yīng)用,也可以選擇遠(yuǎn)程的java服務(wù)來(lái)監(jiān)控,如果監(jiān)控遠(yuǎn)程服務(wù)需要在tomcat啟動(dòng)腳本中添加如下代碼:
-Dcom.sun.management.jmxremote.port=6969 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false連接進(jìn)去之后,就可以看到j(luò)console概覽圖和主要的功能:概述、內(nèi)存、線程、類、VM、MBeans
- 概述,以圖表的方式顯示出堆內(nèi)存使用量,活動(dòng)線程數(shù),已加載的類,CUP占用率的折線圖,可以非常清晰的觀察在程序執(zhí)行過(guò)程中的變動(dòng)情況。
- 內(nèi)存,主要展示了內(nèi)存的使用情況,同時(shí)可以查看堆和非堆內(nèi)存的變化值對(duì)比,也可以點(diǎn)擊執(zhí)行GC來(lái)處罰GC的執(zhí)行
- 線程,主界面展示線程數(shù)的活動(dòng)數(shù)和峰值,同時(shí)點(diǎn)擊左下方線程可以查看線程的詳細(xì)信息,比如線程的狀態(tài)是什么,堆棧內(nèi)容等,同時(shí)也可以點(diǎn)擊“檢測(cè)死鎖”來(lái)檢查線程之間是否有死鎖的情況。
- 類,主要展示已加載類的相關(guān)信息。
- VM 概要,展示JVM所有信息總覽,包括基本信息、線程相關(guān)、堆相關(guān)、操作系統(tǒng)、VM參數(shù)等。
- Mbean,查看Mbean的屬性,方法等。
VisualVM
簡(jiǎn)介
VisualVM 是一個(gè)工具,它提供了一個(gè)可視界面,用于查看 Java 虛擬機(jī) (Java Virtual Machine, JVM) 上運(yùn)行的基于 Java 技術(shù)的應(yīng)用程序(Java 應(yīng)用程序)的詳細(xì)信息。VisualVM 對(duì) Java Development Kit (JDK) 工具所檢索的 JVM 軟件相關(guān)數(shù)據(jù)進(jìn)行組織,并通過(guò)一種使您可以快速查看有關(guān)多個(gè) Java 應(yīng)用程序的數(shù)據(jù)的方式提供該信息。您可以查看本地應(yīng)用程序以及遠(yuǎn)程主機(jī)上運(yùn)行的應(yīng)用程序的相關(guān)數(shù)據(jù)。此外,還可以捕獲有關(guān) JVM 軟件實(shí)例的數(shù)據(jù),并將該數(shù)據(jù)保存到本地系統(tǒng),以供后期查看或與其他用戶共享。
VisualVM 是javajdk自帶的最牛逼的調(diào)優(yōu)工具了吧,也是我平時(shí)使用最多調(diào)優(yōu)工具,幾乎涉及了jvm調(diào)優(yōu)的方方面面。同樣是在jdk/bin目錄下面雙擊jvisualvm.exe既可使用,啟動(dòng)起來(lái)后和jconsole 一樣同樣可以選擇本地和遠(yuǎn)程,如果需要監(jiān)控遠(yuǎn)程同樣需要配置相關(guān)參數(shù),主界面如下;
VisualVM可以根據(jù)需要安裝不同的插件,每個(gè)插件的關(guān)注點(diǎn)都不同,有的主要監(jiān)控GC,有的主要監(jiān)控內(nèi)存,有的監(jiān)控線程等。
如何安裝:
1、從主菜單中選擇“工具”>“插件”。
2、在“可用插件”標(biāo)簽中,選中該插件的“安裝”復(fù)選框。單擊“安裝”。
3、逐步完成插件安裝程序。
大家可能在這里會(huì)遇到安裝不了的坑!!!!
解決:首先用瀏覽器瀏覽:?https://visualvm.github.io/pluginscenters.html
其次從中找到自己的版本,我的是jdk1.8版本,我選擇了第二個(gè),復(fù)制它的地址
復(fù)制地址到上面的url中,就可以去查看可用插件進(jìn)行下載了
這里以IntelliJ Platform (pid 15784)為例,雙擊后直接展開(kāi),主界面展示了系統(tǒng)和jvm兩大塊內(nèi)容,點(diǎn)擊右下方j(luò)vm參數(shù)和系統(tǒng)屬性可以參考詳細(xì)的參數(shù)信息.
因?yàn)閂isualVM的插件太多,我這里主要介紹三個(gè)我主要使用幾個(gè):監(jiān)控、線程、Visual GC
監(jiān)控的主頁(yè)其實(shí)也就是,cpu、內(nèi)存、類、線程的圖表
線程和jconsole功能沒(méi)有太大的區(qū)別
Visual GC 是常常使用的一個(gè)功能,可以明顯的看到年輕代、老年代的內(nèi)存變化,以及gc頻率、gc的時(shí)間等。
以上的功能其實(shí)jconsole幾乎也有,VisualVM更全面更直觀一些,另外VisualVM非常多的其它功能,可以分析dump的內(nèi)存快照,dump出來(lái)的線程快照并且進(jìn)行分析等,還有其它很多的插件大家可以去探索
JVM垃圾回收
JVM的新生代分為三個(gè)區(qū)域,一個(gè)Eden區(qū)和兩個(gè)Survivor區(qū),它們之間的比例為(8:1:1),這個(gè)比例也是可以修改的。通常情況下,對(duì)象主要分配在新生代的Eden區(qū)上,少數(shù)情況下也可能會(huì)直接分配在老年代中。Java虛擬機(jī)每次使用新生代中的Eden和其中一塊Survivor(From),在經(jīng)過(guò)一次Minor GC后,將Eden和Survivor中還存活的對(duì)象一次性地復(fù)制到另一塊Survivor空間上(這里使用的復(fù)制算法進(jìn)行GC),最后清理掉Eden和剛才用過(guò)的Survivor(From)空間。將此時(shí)在Survivor空間存活下來(lái)的對(duì)象的年齡設(shè)置為1,以后這些對(duì)象每在Survivor區(qū)熬過(guò)一次GC,它們的年齡就加1,當(dāng)對(duì)象年齡達(dá)到某個(gè)年齡(默認(rèn)值為15)時(shí),就會(huì)把它們移到老年代中。
在新生代中進(jìn)行GC時(shí),有可能遇到另外一塊Survivor空間沒(méi)有足夠空間存放上一次新生代收集下來(lái)的存活對(duì)象,這些對(duì)象將直接通過(guò)分配擔(dān)保機(jī)制進(jìn)入老年代;
1. 新生代(Young Generation):也有叫做年輕代的,這里使用《深入理解JAVA虛擬機(jī)》中的叫法,下同。
其實(shí)看名稱就能看出一些,一般情況下,新創(chuàng)建的對(duì)象都會(huì)存放到新生代中(大對(duì)象除外)。
新生代中對(duì)象的特點(diǎn)是:很快就會(huì)被GC回收掉的或者不是特別大的對(duì)象。
為了方便垃圾收集,新生代又分出了一個(gè)Eden區(qū),兩個(gè) Survivor區(qū)。
JVM 每次只會(huì)使用 Eden區(qū) 和其中的一塊 Survivor 區(qū)域來(lái)為對(duì)象服務(wù),另一塊Survivor區(qū)域是空的,用于垃圾回收。
舉個(gè)例子,第一次回收的時(shí)候,虛擬機(jī)會(huì)將 Eden區(qū)+Survivor(from)區(qū)域的存活對(duì)象復(fù)制到Survivor(to)上(存活對(duì)象小于Survivor(to)的空間),清空Survivor(from),虛擬機(jī)使用Eden區(qū)+Survivor(to);
第二次回收的時(shí)候,虛擬機(jī)再將Eden區(qū)+Survivor(to)存活的對(duì)象復(fù)制到Survivor(from)。
這三個(gè)區(qū)域默認(rèn)情況下是按照8:1:1分配,也可以手動(dòng)配置。
2.?Eden區(qū)
Eden區(qū)位于Java堆的新生代,是新對(duì)象分配內(nèi)存的地方,由于堆是所有線程共享的,因此在堆上分配內(nèi)存需要加鎖。而Sun JDK為提升效率,會(huì)為每個(gè)新建的線程在Eden上分配一塊獨(dú)立的空間由該線程獨(dú)享,這塊空間稱為T(mén)LAB(Thread Local Allocation Buffer)。在TLAB上分配內(nèi)存不需要加鎖,因此JVM在給線程中的對(duì)象分配內(nèi)存時(shí)會(huì)盡量在TLAB上分配。如果對(duì)象過(guò)大或TLAB用完,則仍然在堆上進(jìn)行分配。如果Eden區(qū)內(nèi)存也用完了,則會(huì)進(jìn)行一次Minor GC(young GC)。
3.Survival from to
Survival區(qū)與Eden區(qū)相同都在Java堆的年輕代。Survival區(qū)有兩塊,一塊稱為from區(qū),另一塊為to區(qū),這兩個(gè)區(qū)是相對(duì)的,在發(fā)生一次Minor GC后,from區(qū)就會(huì)和to區(qū)互換。在發(fā)生Minor GC時(shí),Eden區(qū)和Survivalfrom區(qū)會(huì)把一些仍然存活的對(duì)象復(fù)制進(jìn)Survival to區(qū),并清除內(nèi)存。Survival to區(qū)會(huì)把一些存活得足夠舊的對(duì)象移至年老代。
4. 老年代(Old Generation):在新生代每進(jìn)行一次垃圾收集后,就會(huì)給存活的對(duì)象“加1歲”,當(dāng)年齡達(dá)到一定數(shù)量的時(shí)候就會(huì)進(jìn)入老年代(默認(rèn)是15,可以通過(guò)-XX:MaxTenuringThreshold來(lái)設(shè)置)。
另外,比較大的對(duì)象也會(huì)進(jìn)入老年代,可以-XX:PretenureSizeThreshold進(jìn)行設(shè)置。
如-XX:PretenureSizeThreshold3M,那么大于3M的對(duì)象就會(huì)直接就進(jìn)入老年代。
因此,老年代中存放的都是一些生命周期較長(zhǎng)的對(duì)象或者特別大的對(duì)象。
5. 永久代(Permanent Generation ):即JVM的方法區(qū)。在這里存放著一些被虛擬機(jī)加載的類信息(別忘了還有動(dòng)態(tài)生成的類)的靜態(tài)文件,這就導(dǎo)致了這個(gè)區(qū)中的東西比老年代和新生代更不容易回收。
永久代大小通過(guò)-XX:MaxPermSize=<N>進(jìn)行設(shè)置。
6. 元空間(Metaspace):從JDK 8開(kāi)始,Java開(kāi)始使用元空間取代永久代,元空間并不在虛擬機(jī)中,而是直接使用本地內(nèi)存。
那么,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存限制。當(dāng)然,也可以對(duì)元空間的大小手動(dòng)的配置。
JVM常見(jiàn)的垃圾回收機(jī)制
Minor GC
Minor GC指新生代GC,即發(fā)生在新生代(包括Eden區(qū)和Survivor區(qū))的垃圾回收操作,當(dāng)新生代無(wú)法為新生對(duì)象分配內(nèi)存空間的時(shí)候,會(huì)觸發(fā)Minor GC。因?yàn)樾律写蠖鄶?shù)對(duì)象的生命周期都很短,所以發(fā)生Minor GC的頻率很高,雖然它會(huì)觸發(fā)stop-the-world,但是它的回收速度很快。
Major GC
Major GC清理Tenured區(qū),用于回收老年代,出現(xiàn)Major GC通常會(huì)出現(xiàn)至少一次Minor GC。
Full GC
Full GC是針對(duì)整個(gè)新生代、老生代、元空間(metaspace,java8以上版本取代perm gen)的全局范圍的GC。Full GC不等于Major GC,也不等于Minor GC+Major GC,發(fā)生Full GC需要看使用了什么垃圾收集器組合,才能解釋是什么樣的垃圾回收。
復(fù)制算法:
將區(qū)域分成兩部分,其中一部分作為保留空間,另一部分作為使用空間、當(dāng)發(fā)生?垃圾回收?時(shí),首先檢查使用空間里有哪些對(duì)象是存活的,檢查完之后把存活的對(duì)象復(fù)制到保留空間(這樣復(fù)制過(guò)來(lái)的好處是減少了內(nèi)存碎片,如果直接在使用空間清除的話,那空間會(huì)很零散)里,然后清洗使用空間。
Survivor的存在意義,就是減少被送到老年代的對(duì)象,進(jìn)而減少Full GC的發(fā)生,Survivor的預(yù)篩選保證,只有經(jīng)歷(默認(rèn))15次Minor GC還能在新生代中存活的對(duì)象,才會(huì)被送到老年代。
為什么要設(shè)置兩個(gè)Survivor區(qū)
設(shè)置兩個(gè)Survivor區(qū)最大的好處就是解決了碎片化?,下面我們來(lái)分析一下。
為什么一個(gè)Survivor區(qū)不行?第一部分中,我們知道了必須設(shè)置Survivor區(qū)。假設(shè)現(xiàn)在只有一個(gè)survivor區(qū),我們來(lái)模擬一下流程:
剛剛新建的對(duì)象在Eden中,一旦Eden滿了,觸發(fā)一次Minor GC,Eden中的存活對(duì)象就會(huì)被移動(dòng)到Survivor區(qū)。這樣繼續(xù)循環(huán)下去,下一次Eden滿了的時(shí)候,問(wèn)題來(lái)了,此時(shí)進(jìn)行Minor GC,Eden和Survivor各有一些存活對(duì)象,如果此時(shí)把Eden區(qū)的存活對(duì)象硬放到Survivor區(qū),很明顯這兩部分對(duì)象所占有的內(nèi)存是不連續(xù)的,也就導(dǎo)致了內(nèi)存碎片化。?
我繪制了一幅圖來(lái)表明這個(gè)過(guò)程。其中色塊代表對(duì)象,白色框分別代表Eden區(qū)(大)和Survivor區(qū)(小)。Eden區(qū)理所當(dāng)然大一些,否則新建對(duì)象很快就導(dǎo)致Eden區(qū)滿,進(jìn)而觸發(fā)Minor GC,有悖于初衷。?
在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流群。交流學(xué)習(xí)群號(hào):821169538 ?里面會(huì)分享一些資深架構(gòu)師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識(shí)體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多。
碎片化帶來(lái)的風(fēng)險(xiǎn)是極大的,嚴(yán)重影響JAVA程序的性能。堆空間被散布的對(duì)象占據(jù)不連續(xù)的內(nèi)存,最直接的結(jié)果就是,堆中沒(méi)有足夠大的連續(xù)內(nèi)存空間,接下去如果程序需要給一個(gè)內(nèi)存需求很大的對(duì)象分配內(nèi)存。。。畫(huà)面太美不敢看。。。這就好比我們爬山的時(shí)候,背包里所有東西緊挨著放,最后就可能省出一塊完整的空間放相機(jī)。如果每件行李之間隔一點(diǎn)空隙亂放,很可能最后就要一路把相機(jī)掛在脖子上了。
那么,順理成章的,應(yīng)該建立兩塊Survivor區(qū),剛剛新建的對(duì)象在Eden中,經(jīng)歷一次Minor GC,Eden中的存活對(duì)象就會(huì)被移動(dòng)到第一塊survivor space S0,Eden被清空;等Eden區(qū)再滿了,就再觸發(fā)一次Minor GC,Eden和S0中的存活對(duì)象又會(huì)被復(fù)制送入第二塊survivor space S1(這個(gè)過(guò)程非常重要,因?yàn)檫@種復(fù)制算法保證了S1中來(lái)自S0和Eden兩部分的存活對(duì)象占用連續(xù)的內(nèi)存空間,避免了碎片化的發(fā)生)。S0和Eden被清空,然后下一輪S0與S1交換角色,如此循環(huán)往復(fù)。如果對(duì)象的復(fù)制次數(shù)達(dá)到16次,該對(duì)象就會(huì)被送到老年代中。下圖中每部分的意義和上一張圖一樣,就不加注釋了。
上述機(jī)制最大的好處就是,整個(gè)過(guò)程中,永遠(yuǎn)有一個(gè)survivor space是空的,另一個(gè)非空的survivor space無(wú)碎片。
轉(zhuǎn)載于:https://my.oschina.net/u/3972077/blog/2243643
總結(jié)
以上是生活随笔為你收集整理的一文详解java中对JVM的深度解析、调优工具、垃圾回收的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux 指定运行时动态库路径【转】
- 下一篇: 做梦梦到洗头是什么意思