運(yùn)維必會JVM知識

Java的GC機(jī)制

回收的對象:不存在任何引用的對象

堆區(qū)(Heap)

堆區(qū)是GC最頻繁的,也是理解GC機(jī)制最重要的區(qū)域。堆區(qū)由所有線程共享,在虛擬機(jī)啟動時(shí)創(chuàng)建。堆區(qū)主要用于存放對象實(shí)例及數(shù)組,所有new出來的對象都存儲在該區(qū)域。

如何判斷對象是垃圾 ?

引用計(jì)數(shù)算法
經(jīng)典的引用計(jì)數(shù)算法,每個(gè)對象添加到引用計(jì)數(shù)器,每被引用一次,計(jì)數(shù)器+1,失去引用,計(jì)數(shù)器-1,當(dāng)計(jì)數(shù)器在一段時(shí)間內(nèi)為0時(shí),即認(rèn)為該對象可以被回收了。但是這個(gè)算法有個(gè)明顯的缺陷:當(dāng)兩個(gè)對象相互引用,但是二者都已經(jīng)沒有作用時(shí),理應(yīng)把它們都回收,但是由于它們相互引用,不符合垃圾回收的條件,所以就導(dǎo)致無法處理掉這一塊內(nèi)存區(qū)域。因此,Sun的JVM并沒有采用這種算法,而是采用一個(gè)叫——根搜索算法,如圖:

image

根搜索算法

基本思想是:從一個(gè)叫GC Roots的根節(jié)點(diǎn)出發(fā),向下搜索,如果一個(gè)對象不能達(dá)到GC Roots的時(shí)候,說明該對象不再被引用,可以被回收。如上圖中的Object5、Object6、Object7,雖然它們?nèi)齻€(gè)依然相互引用,但是它們其實(shí)已經(jīng)沒有作用了,這樣就解決了引用計(jì)數(shù)算法的缺陷。

內(nèi)存分代

JVM區(qū)域總體分兩類,heap區(qū)和非heap區(qū)。
heap區(qū)又分為:

Eden Space(伊甸園)、

對象被創(chuàng)建的時(shí)候首先放到這個(gè)區(qū)域,進(jìn)行垃圾回收后,不能被回收的對象被放入到空的survivor區(qū)域

Survivor Space(幸存者區(qū))、

用于保存在eden space內(nèi)存區(qū)域中經(jīng)過垃圾回收后沒有被回收的對象

回收的時(shí)候Eden區(qū)域不能被回收的對象被放入到空的survivor(也就是To Survivor,同時(shí)Eden區(qū)域的內(nèi)存會在垃圾回收的過程中全部釋放),
另一個(gè)survivor(即From Survivor)里不能被回收的對象也會被放入這個(gè)survivor(即To Survivor),然后To Survivor 和 From Survivor的標(biāo)記會互換,始終保證一個(gè)survivor是空的,涉及到一個(gè)算法

Eden SpaceSurvivor Space都屬于新生代,新生代中執(zhí)行的垃圾回收被稱之為Minor GC(因?yàn)槭菍π律M(jìn)行垃圾回收,所以又被稱為Young GC),每一次Young GC后留下來的對象age加1

新生代進(jìn)行垃圾回收時(shí)會出發(fā)Minor GC(也稱作Young GC

Old Gen(老年代)。

非heap區(qū)又分:

Code Cache(代碼緩存區(qū));
Perm Gen(永久代);

(Perm Gen全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域)
Jvm Stack(java虛擬機(jī)棧);
默認(rèn)大小為物理內(nèi)存的1/64。
不會被JVM垃圾回收

老年代用于存放新生代多次回收依然存活的對象,如緩存對象。老年代滿了的時(shí)候就需要對老年代進(jìn)行回收,老年代的垃圾回收稱作Major GC(也稱作Full GC
永久代是有大小限制的,因此如果加載的類太多,很有可能導(dǎo)致永久代內(nèi)存溢出

Local Method Statck(本地方法棧);

垃圾回收器

1.串行收集
2.并行收集
3.CMS收集器
4.G1收集器

使用G1收集器時(shí),Java堆的內(nèi)存布局與其他收集器有很大差別,它將整個(gè)Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔閡了,它們都是一部分(可以不連續(xù))Region的集合。

GC常見算法?

1.復(fù)制算法
2.標(biāo)記--清除算法
3.標(biāo)記--壓縮算法
4.分代回收算法

JVM參數(shù)調(diào)優(yōu)

image.png

注:jdk8 開始,用 MetaSpace (元空間)區(qū)取代了 Perm 區(qū)(永久代),所以相應(yīng)的 jvm 參數(shù)變成 -XX:MetaspaceSize 及 -XX:MaxMetaspaceSize。

MetaSpace GC

如果Metaspace的空間占用達(dá)到了設(shè)定的最大值,那么就會觸發(fā)GC來收集死亡對象和類的加載器。根據(jù)JDK 8的特性,G1和CMS都會很好地收集Metaspace區(qū)(一般都伴隨著Full GC)。

為了減少垃圾回收的頻率及時(shí)間,控制吞吐量,對Metaspace進(jìn)行適當(dāng)?shù)谋O(jiān)控和調(diào)優(yōu)是非常有必要的。如果在Metaspace區(qū)發(fā)生了頻繁的Full GC,那么可能表示存在內(nèi)存泄露或Metaspace區(qū)的空間太小了。

新增的 JVM 參數(shù)

-XX:MetaspaceSize 是分配給類元數(shù)據(jù)空間(以字節(jié)計(jì))的初始大小(Oracle邏輯存儲上的初始高水位,the initial high-water-mark ),此值為估計(jì)值。MetaspaceSize的值設(shè)置的過大會延長垃圾回收時(shí)間。垃圾回收過后,引起下一次垃圾回收的類元數(shù)據(jù)空間的大小可能會變大。

-XX:MaxMetaspaceSize 是分配給類元數(shù)據(jù)空間的最大值,超過此值就會觸發(fā)Full GC,此值默認(rèn)沒有限制,但應(yīng)取決于系統(tǒng)內(nèi)存的大小。JVM會動態(tài)地改變此值。

-XX:MinMetaspaceFreeRatio 表示一次GC以后,為了避免增加元數(shù)據(jù)空間的大小,空閑的類元數(shù)據(jù)的容量的最小比例,不夠就會導(dǎo)致垃圾回收。

-XX:MaxMetaspaceFreeRatio 表示一次GC以后,為了避免增加元數(shù)據(jù)空間的大小,空閑的類元數(shù)據(jù)的容量的最大比例,不夠就會導(dǎo)致垃圾回收。

JVM參數(shù)優(yōu)化
image.png

調(diào)優(yōu)原則

調(diào)優(yōu)方法以及原則一切都是為了這一步,調(diào)優(yōu),在調(diào)優(yōu)之前,我們需要記住下面的原則:

1、多數(shù)的Java應(yīng)用不需要在服務(wù)器上進(jìn)行GC優(yōu)化;
2、多數(shù)導(dǎo)致GC問題的Java應(yīng)用,都不是因?yàn)槲覀儏?shù)設(shè)置錯(cuò)誤,而是代碼問題;
3、在應(yīng)用上線之前,先考慮將機(jī)器的JVM參數(shù)設(shè)置到最優(yōu)(最適合);
4、減少創(chuàng)建對象的數(shù)量;
5、減少使用全局變量和大對象;
6、GC優(yōu)化是到最后不得已才采用的手段;
7、在實(shí)際使用中,分析GC情況優(yōu)化代碼比優(yōu)化GC參數(shù)要多得多;

Java虛擬機(jī)參數(shù)設(shè)置:
(1)性能參數(shù):

-server

以server模式運(yùn)行時(shí)將擁有:更大、更高的并發(fā)處理能力,更快更強(qiáng)捷的JVM垃圾回收機(jī)制,可以獲得更多的負(fù)載與吞吐量

-Xmx
  指定java程序的最大堆內(nèi)存, 使用java -Xmx5000M -version判斷當(dāng)前系統(tǒng)能分配的最大堆內(nèi)存

-Xms
  指定最小堆內(nèi)存, 通常設(shè)置成跟最大堆內(nèi)存一樣,減少GC

-Xmn
  設(shè)置年輕代大小為512m。整個(gè)堆大小=年輕代大小 + 年老代大小。所以增大年輕代后,將會減小年老代大小。此值對系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8。

-Xss
  指定線程的最大??臻g, 此參數(shù)決定了java函數(shù)調(diào)用的深度, 值越大調(diào)用深度越深, 若值太小則容易出棧溢出錯(cuò)誤(StackOverflowError)
設(shè)定每個(gè)線程的堆棧大小。這個(gè)就要依據(jù)你的程序,看一個(gè)線程 大約需要占用多少內(nèi)存,可能會有多少線程同時(shí)運(yùn)行等。一般不易設(shè)置超過1M,要不然容易出現(xiàn)out ofmemory

-XX:PermSize
  指定方法區(qū)(永久區(qū))的初始值,默認(rèn)是物理內(nèi)存的1/64, 在Java8永久區(qū)移除, 代之的是元數(shù)據(jù)區(qū), 由-XX:MetaspaceSize指定

-XX:MaxPermSize
  指定方法區(qū)的最大值, 默認(rèn)是物理內(nèi)存的1/4, 在java8中由-XX:MaxMetaspaceSize指定元數(shù)據(jù)區(qū)的大小

-XX:NewRatio=n
  年老代與年輕代的比值,-XX:NewRatio=2, 表示年老代與年輕代的比值為2:1

-XX:SurvivorRatio=n
  Eden區(qū)與Survivor區(qū)的大小比值,-XX:SurvivorRatio=8表示Eden區(qū)與Survivor區(qū)的大小比值是8:1:1,因?yàn)镾urvivor區(qū)有兩個(gè)(from, to)

-XX:+DoEscapeAnalysis
  開啟逃逸分析, 逃逸分析的目的是判斷對象的作用域是否可能逃逸出函數(shù)體, 逃逸分析是棧上分配的技術(shù)基礎(chǔ),對于非逃逸對象而言就是一個(gè)局部變量, 而對象未發(fā)生逃逸時(shí), 虛擬機(jī)就有可能進(jìn)行線上分配, 不是堆上, 棧上分配速度快,并且能避免垃圾回收帶來的負(fù)面影響, 棧上分配是虛擬機(jī)提供的很好的對象分配優(yōu)化策略

-XX:+EliminateAllocations
  開啟標(biāo)量替換(默認(rèn)打開), 即允許對象打散分配在棧上, 即對象的屬性視為獨(dú)立局部變量進(jìn)行分配到棧上

-XX:+UseCompressedOops
  開啟指針壓縮

-XX:+AggressiveOpts
  啟用JVM開發(fā)團(tuán)隊(duì)最新的調(diào)優(yōu)成果。例如編譯優(yōu)化,偏向鎖,并行年老代收集等

-XX:-UseTLAB
  關(guān)閉TLAB , 默認(rèn)是打開的

-Djava.awt.headless=true
  有時(shí)我們會在我們的J2EE工程中使用一些圖表工具如:jfreechart,用于在web網(wǎng)頁輸出GIF/JPG等流,在winodws環(huán)境下,一般我 們的app server在輸出圖形時(shí)不會碰到什么問題,但是在linux/unix環(huán)境下經(jīng)常會碰到一個(gè)exception導(dǎo)致你在winodws開發(fā)環(huán)境下圖片顯 示的好好可是在linux/unix下卻顯示不出來,因此加上這個(gè)參數(shù)以免避這樣的情況出現(xiàn).

-XX:+DisableExplicitGC
  在程序代碼中不允許有顯示的調(diào)用”System.gc()”??吹竭^有兩個(gè)極品工程中每次在DAO操作結(jié)束時(shí)手動調(diào)用System.gc()一下,覺得這 樣做好像能夠解決它們的out ofmemory問題一樣,付出的代價(jià)就是系統(tǒng)響應(yīng)時(shí)間嚴(yán)重降低,就和我在關(guān)于Xms,Xmx里的解釋的原理一樣,這樣去調(diào)用GC導(dǎo)致系統(tǒng)的JVM大起大 落,性能不到什么地方去喲!

-XX:+UseBiasedLocking
  偏向鎖,啟用一個(gè)優(yōu)化了的線程鎖,我們知道在我們的appserver,每個(gè)http請求就是一個(gè)線程,有的請求短有的請求長,就會有請求排隊(duì)的現(xiàn)象,甚至還會出現(xiàn)線程阻塞,這個(gè)優(yōu)化了的線程鎖使得你的appserver內(nèi)對線程處理自動進(jìn)行最優(yōu)調(diào)配。

GC策略

策略 1:將新對象預(yù)留在新生代,由于 Full GC 的成本遠(yuǎn)高于 Minor GC,因此盡可能將對象分配在新生代是明智的做法,實(shí)際項(xiàng)目中根據(jù) GC 日志分析新生代空間大小分配是否合理,適當(dāng)通過“-Xmn”命令調(diào)節(jié)新生代大小,最大限度降低新對象直接進(jìn)入老年代的情況。

策略 2:大對象進(jìn)入老年代,雖然大部分情況下,將對象分配在新生代是合理的。但是對于大對象這種做法卻值得商榷,大對象如果首次在新生代分配可能會出現(xiàn)空間不足導(dǎo)致很多年齡不夠的小對象被分配的老年代,破壞新生代的對象結(jié)構(gòu),可能會出現(xiàn)頻繁的 full gc。因此,對于大對象,可以設(shè)置直接進(jìn)入老年代(當(dāng)然短命的大對象對于垃圾回收老說簡直就是噩夢)。-XX:PretenureSizeThreshold 可以設(shè)置直接進(jìn)入老年代的對象大小。

策略 3:合理設(shè)置進(jìn)入老年代對象的年齡,-XX:MaxTenuringThreshold 設(shè)置對象進(jìn)入老年代的年齡大小,減少老年代的內(nèi)存占用,降低 full gc 發(fā)生的頻率。

策略 4:設(shè)置穩(wěn)定的堆大小,堆大小設(shè)置有兩個(gè)參數(shù):-Xms 初始化堆大小,-Xmx 最大堆大小。

策略5:注意:如果滿足下面的指標(biāo),則一般不需要進(jìn)行 GC 優(yōu)化:

MinorGC 執(zhí)行時(shí)間不到50ms;

Minor GC 執(zhí)行不頻繁,約10秒一次;

Full GC 執(zhí)行時(shí)間不到1s;

Full GC 執(zhí)行頻率不算頻繁,不低于10分鐘1次。

監(jiān)控JVM

一,需求:大數(shù)據(jù)的機(jī)器,很多jvm進(jìn)程,需要監(jiān)控特定的幾個(gè)的進(jìn)程的FGC,FGCT,YGC,YGCT,GCT。

YGC:從應(yīng)用程序啟動到采樣時(shí)年輕代中g(shù)c次數(shù)
YGCT:從應(yīng)用程序啟動到采樣時(shí)年輕代中g(shù)c所用時(shí)間(s)
FGC:從應(yīng)用程序啟動到采樣時(shí)old代(全gc)gc次數(shù)
FGCT:從應(yīng)用程序啟動到采樣時(shí)old代(全gc)gc所用時(shí)間(s)
GCT:從應(yīng)用程序啟動到采樣時(shí)gc用的總時(shí)間(s)

二 編寫自定義監(jiān)控項(xiàng)腳步

jps命令可以獲取到進(jìn)程的ID,jstat -gc ID ,獲取gc的信息
參數(shù)詳解
jstat參考

cat jps1.py
#!/usr/bin/env python
import os,sys

jps_info = os.popen("/usr/bin/sudo /usr/local/java/bin/jps | grep %s | awk '{print $1}'"% sys.argv[1])
jps_info = jps_info.read()
jps_id = jps_info.strip()

jstat = {'YGC':'$13','YGCT':'$14','FGC':'$15','FGCT':'$16','GCT':'$17'}

info = jstat[sys.argv[2]]
command = "/usr/bin/sudo /usr/local/java/bin/jstat -gc "+jps_id+" | awk '{print "+info+"}'| /usr/bin/tail -n 1"

os.system(command)

三,編輯zabbix agent配置文件

添加:UserParameter=jstat[*], /usr/bin/python /etc/zabbix/scripts/jps1.py $1 $2

四,其它地方的調(diào)整

在本機(jī)測試腳本是正常的,但是在zabbix server上測試有幾個(gè)問題需要修改

編輯 /etc/sudoers文件,添加一行:

  zabbix    ALL=(ALL)       NOPASSWD:ALL

  這一行需要注釋掉:#Defaults    requiretty  

五,創(chuàng)建監(jiān)控項(xiàng)(數(shù)據(jù)類型選擇浮點(diǎn)數(shù))

image

六,添加模板展示

image

如何修改JVM參數(shù)

在安裝目錄的bin/catalina.sh 文件的cygwin=false上面加上:

JAVA_OPTS="-Xms256m -Xmx2048m -XX:PermSize=128m -XX:MaxPermSize=512m"
或者
CATALINA_OPTS="-Xms256m -Xmx2048m -XX:PermSize=128m -XX:MaxPermSize=512m"

參考:
https://www.sczyh30.com/posts/Java/jvm-metaspace/
http://www.itdecent.cn/p/bc2e4d4ff018

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Java 虛擬機(jī)有自己完善的硬件架構(gòu), 如處理器、堆棧、寄存器等,還具有相應(yīng)的指令系統(tǒng)。JVM 屏蔽了與具體操作系...
    尹小凱閱讀 1,748評論 0 10
  • 轉(zhuǎn)載blog.csdn.net/ning109314/article/details/10411495/ JVM工...
    forever_smile閱讀 5,505評論 1 56
  • JVM架構(gòu) 當(dāng)一個(gè)程序啟動之前,它的class會被類裝載器裝入方法區(qū)(Permanent區(qū)),執(zhí)行引擎讀取方法區(qū)的...
    cocohaifang閱讀 1,825評論 0 7
  • 內(nèi)存溢出和內(nèi)存泄漏的區(qū)別 內(nèi)存溢出:out of memory,是指程序在申請內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用,...
    Aimerwhy閱讀 800評論 0 1
  • GC區(qū)域Eden Survivor(from,to), Old Gen和Perm Gen VM區(qū)域總體分兩類,he...
    Fitz_Lee閱讀 482評論 0 0

友情鏈接更多精彩內(nèi)容