jvm專欄
類加載的流程
加載
首先你的代碼中包含“main()”方法的主類一定會在JVM進程啟動之后被加載到內存,開始執(zhí)行你的“main()”方法中
的代碼台夺。接著遇到你使用了別的類毛甲,比如“ReplicaManager”川背,此時就會從對應的“.class”字節(jié)碼文件加載對應的類到內存里來砚作。
驗證
就是根據java虛擬機的規(guī)范,來校驗你加載進來的class文件內容是否符合指定的規(guī)范碍沐。如果你的class文件被人篡改,jvm是沒法執(zhí)行的衷蜓,所以加載.class文件到內存后,必須先驗證一下尘喝,然后才能交給jvm運行
準備
其實就是給這個ReplicaManager分配內存磁浇,然后給static修飾的變量(類變量)賦默認的初始值
解析
符合引用替換成直接引用的過程
初始化
再這個階段,就會執(zhí)行類的初始化代碼朽褪,準備的階段的時候只是分配的內存和給個初始值0置吓,初始化階段是完成配置項的讀取,然后在賦值缔赠。如果發(fā)現父類還沒加載和初始化衍锚,先加載父類和初始化父類然后再初始化子類
使用
卸載
類加載器
bootstrap classloader(啟動類加載器)
它負責加載我們機器安裝的java目錄下的核心類,一旦jvm啟動嗤堰,首先會依托啟動類加載器去加載你java目錄下的lib目錄中的核心類庫
extension classloader(擴展類加載器)
你的java目錄有一個lib\ext目錄戴质,里面有寫類是需要這個加載器加載的,支撐你的系統(tǒng)運行
application classloader(應用類加載器)
這個加載器是負責加載指定classpth環(huán)境變量所指定的路徑中的類踢匣。大概就是去加載你寫好的代碼告匠,這個加載器就負責把你寫好的代碼加載到內存中
自定義加載器
根據你自己的需求去加載類
雙親委派機制
jvm的加載器是有親子層級的,第一層是啟動類加載器离唬,第二層是擴展類加載器后专,第三層是應用類加載器,自定義加載器再最下層
流程:
比如你的應用類程序加載器需要加載一個類输莺,首先他會委派他的父類加載器去加載戚哎,最終會傳導到頂層的類加載器去加載。如果父類加載器的負責的加載范圍內沒有找到這個類嫂用,會下推加載權利給自己的子類加載器
好處:
先找父類加載型凳,不行的話再去找子類加載,這樣就避免了多層級的類加載器重復加載的問題
JVM的內存區(qū)域
metaspace(元空間)
存放各種類的信息
程序計數器
記錄當前執(zhí)行字節(jié)碼指令的位置
java虛擬機棧
保存每個方法的局部變量尸折。如果線程執(zhí)行了一個方法啰脚,就會對這個方法創(chuàng)建一個棧幀。棧幀包括局部變量表实夹,操作數棧橄浓,動態(tài)鏈接,方法出口等等
堆內存
存放創(chuàng)建的各種對象
流程:
1.jvm啟動亮航,先加載你的kafka類到內存荸实,然后有個main方法,開始執(zhí)行你的main方法缴淋,main線程的關聯了程序計數器的准给,會記錄你執(zhí)行到哪一行指令
2.main線程再執(zhí)行main方法的時候泄朴,會再main線程關聯的java虛擬機棧里,會壓入一個main方法的棧幀露氮,接著發(fā)現需要創(chuàng)建ReplicaManger類的實例對象祖灰,會加載這個類到內存中。然后對象實例分配再堆內存中畔规,并再main方法的棧幀的局部變量表引入一個replicaManager變量局扶。讓他引用ReplicaManager這個對象再堆內存中的地址
3.main線程就執(zhí)行ReplicaManager中的方法,會依次把執(zhí)行到的方法對應的棧幀壓入自己的虛擬機棧叁扫,執(zhí)行完之后再把對應方法的棧幀從java虛擬機棧中出棧
JVM核心參數
-Xms
java堆內存大小
-XX:InitialHeapSize
初始化堆大小
-XmX
java堆內存的最大大小
-Xmn
java堆內存的新生代大小三妈,扣除新生代的就是老年代的大小
-XX:PermSize
永久代大小(方法區(qū))1.8以后 -XX:MetaspaceSize
-XX:MaxPermSize:
永久代最大大小 1.8以后 和-XX:MaxMetaspaceSize
-Xss
每個線程的棧內存大小
-XX:+PrintGCDetils:
打印詳細的gc日志
-XX:+PrintGCTimeStamps
打印每次gc發(fā)生的時間
-XX:+UseParNewGC
指定垃圾回收器
“-XX:+UseG1GC
指定使用G1垃圾回收器莫绣,此時會自動用堆大小除以2048
XX:MaxGCPauseMills”
最大停頓時間 默認值是200ms
-XX:CMSInitiatingOccupancyFaction
老年代使用了多少比例的內存畴蒲,就開始full gc, 默認92%
-XX:UseCMSCompactAtFullCollection
每次full gc之后,都需要內存碎片整理
-XX:CMSFullGCsBeforeCompaction
執(zhí)行多少次Full GC之后再執(zhí)行一次內存碎片整理的工作对室,默認
是0模燥,意思就是每次Full GC之后都會進行一次內存整理。
-XX:MaxTenuringThreshold
最大分代年齡 掩宜,默認15
-XX:PretenureSizeThreshold
大對象直接進入老年代
“-XX:G1HeapWastePercent
默認值是5%
空閑出來的Region數量達到了堆內存的5%涧窒,此時就會 立即停止混合回收
-XX:G1MixedGCLiveThresholdPercent
他的默認值是85%,意思就是確定要回收的Region的時候锭亏,必須是存
活對象低于85%的Region才可以進行回收
-XX:+CMSParallelinitialMarkEnabled
再CMS垃圾回收的初始標記階段開啟多線程并發(fā)執(zhí)行纠吴,減少stop the world時間
-XX:+CMSScavengeBeforeRemark
再CMS的重新標記階段之前,先盡量執(zhí)行一次young gc
好處:先執(zhí)行一次young gc就會回收掉一些年輕代沒有人引用的對象慧瘤,那么再CMS重新標記階段就可以少掃描一些對象戴已,提示CMS重新標記階段的性能,減少他的耗時
-XX:+DisableExplicitGC
禁止顯示執(zhí)行gc .比如System.gc()
格式
-Xms512M -Xmx512M -Xmn256M -Xss1M -XX:PermSize=128M -XX:MaxPermSize=128M
模板
“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -
XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -
XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92 -XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSParallelinitialMarkEnabled -XX:+CMSScavengeBeforeRemark”
jvm的四種引用
強引用
1個變量引用一個對象锅减,只要是強引用對象糖儡,垃圾回收的時候絕對不會回收這個對象
軟引用
1個對象被“SoftReference”軟引用類型的對象包裹起來了。正常垃圾回收是不會回收軟引用的對象的怔匣,但是垃圾回收之后發(fā)現內存空間不夠存放新的對象握联,內存快要溢出了,就會把這些軟引用到的對象回收每瞒。也就是能存活到下一次垃圾回收的時候
弱引用
弱引用就和沒引用一起金闽,垃圾回收的時候就會把這個對象回收
虛引用
很少用
young gc日志查看
GC (Allocation Failure
對象分配失
敗。
ParNew: 4030K->512K(4608K), 0.0015734 secs
1.指定的ParNew垃圾回收器執(zhí)行GC
- 4608K也就是4.5M剿骨,Eden+1個S的大小
- 4030K->512K 對年輕代執(zhí)行了一次GC代芜,GC之前使用了4030K,但是GC之后只有512K的對象存活下來
- 0.0015734 secs 本次Gc耗費的時間浓利,大概1.5ms,僅僅回收3M的對象
4030K->574K(9728K), 0.0017518 secs (描述整個堆內存的情況)
9728K(9.5M) 堆內存的總可用空間挤庇, 其實就是年輕代4.5M+老年代5M钞速,然后GC之前整個堆內存使用了4030K,GC后java堆內存使用了574K
par new generation total 4608K, used 2601K
ParNew垃圾回收器負責的年輕代總共有4608k(4.5M)可用內存嫡秕,目前使用了2601K(2.5M)
eden space 4096K, 51% used
Eden區(qū)有4M的內存渴语,被使用了51%
from space 512K, 100% used
from survivor區(qū),512K是100%使用率
to space 512K, 0% used
to survivor區(qū)昆咽, 512K是0%使用率
concurrent mark-sweep generation total 5120K, used 62K
concurrent Mark Sweep(CMS)垃圾回收器,管理老年代的內存空間一共是5M遵班,此時使用了62K
CMS: 8194K->6836K(10240K), 0.0049920 secs 老年代gc
老年代從8m對象的占用變成了6M 的對象占用
jvm分析
jstat -gc PID
S0C
from survivor區(qū)的大小
S1C
to survivor區(qū)的大小
S0U
from survivor區(qū)當前使用的內存大小
S1C
to survivor區(qū)當前使用的內存大小
EC
Eden區(qū)的大小
EU
Eden區(qū)當前使用的內存大小
OC
老年代的大小
OU
老年代當前使用的內存大小
MC
方法區(qū)(永久代,元數據區(qū))的大小
MU
方法區(qū)(永久代潮改,元數據區(qū))當前使用內存的大小
YGC
系統(tǒng)運行迄今為止的young gc 的次數
YGCT
young gc的耗時
FGC
系統(tǒng)運行迄今為止的full gc 的次數
FGCT
full gc的耗時
GCT
所有GC的總耗時
新生代對象增長的速率,young gc觸發(fā)頻率腹暖, young gc的耗時汇在,每次young gc后有多少對象是否存活下來,每次young gc過后有多少對象進入了老年代脏答,老年代對象的增長速率糕殉, full gc的觸發(fā)頻率,full gc的耗時
新生代對象增長的速率
你只要再線上linux機器上運行:jstat -gc pid 10 每隔1秒更新出來最新的一行jstat統(tǒng)計信息殖告,一共執(zhí)行10次jstat統(tǒng)計 jstat -gc PID 1000 1000 每隔1秒鐘打印一次阿蝶,連續(xù)打印1000次
例子:比如第一秒顯示eden區(qū)使用了200M內存,第二秒顯示出來的統(tǒng)計信息里發(fā)現Eden區(qū)使用了205M黄绩,第三秒發(fā)現Eden區(qū)使用了209M內存羡洁,依次類推,推斷出來這個系統(tǒng)每秒新增5M左右的對象
young gc的觸發(fā)頻率和每次耗時
比如Eden有800M內存爽丹,發(fā)現高峰期每秒新增5M對象筑煮,大概高峰期3分鐘會觸發(fā)一個young gc. 日常期每秒新增0.5M對象,那么日常期大概需要半個小時才會觸發(fā)一次young gc
jstat會告訴你迄今為止系統(tǒng)發(fā)生了多少次young gc和這些young c的耗時粤蝎。 比如系統(tǒng)運行24小時發(fā)生了260次young gc ,總耗時20s.那么平均下來每次young gc大概就耗時幾十毫秒
每次young gc有多少對象是存活和進入老年代的
之前我們推算出來高峰期的時候多久發(fā)生異常young gc,比如3分鐘會有一次真仲,那么我們執(zhí)行 jstat -gc pid 180000 10.就相當于每隔3分鐘執(zhí)行一次統(tǒng)計,連續(xù)執(zhí)行10次初澎。此時大家可以觀察秸应,每隔3分鐘之后發(fā)生了一次young gc,此時Eden碑宴,Surivivor 老年代的對象變化软啼。正常情況下,老年代的增長不可能快速的延柠,如果你發(fā)現每次young gc后焰宣,每次老年代新增幾十M,很有可能young gc存活的對象太多了捕仔。
full gc的觸發(fā)時機和耗時
比如老年代的總內存800M匕积,每隔3分鐘新增50M對象盈罐,大概1個小時就會觸發(fā)full gc∩了簦可以根據jstact可以知道full gc的次數以及總耗時盅粪,比如full gc執(zhí)行了10次,總共耗時30s,大概每次full gc的時間3s左右
jmap和jhat
jmap -heap PID
會打印堆內存的各個區(qū)域的情況
jmap -histo PID
按照各種對象占用內存空間的大小降序排列悄蕾,把占用內存最多的對象放在最上面
使用jmap生成堆內存轉儲快照
jmap -dump:live,format=b,file-dump.hprof PID
這個命令會再當前目錄下生成一個dump.hprof文件,需要用jhat在瀏覽器中分析堆快照
jhat -port 8000 dump.hprof
訪問http://ip:8000
優(yōu)化思路:
盡量讓每次young gc后的存活對象小于Survivor區(qū)域的50%, 都留存在年輕代里票顾,盡量別讓對象進入老年代,盡量減少full gc的頻率帆调, 避免頻繁full gc對jvm性能的影響
堆內存溢出
老年代放了過多的對象奠骄,而且大多數都是存活的,即使GC過后還是大部分都存活番刊,所以要繼續(xù)放入更多的對象已經不可能了含鳞,只能引發(fā)內存溢出問題
垃圾回收算法
復制算法(針對新生代的)
把新生代的內存區(qū)域分為2部分,然后只使用其中一塊芹务,待內存快滿的時候蝉绷,就把里面存活的對象轉移到另一塊內存區(qū)域,保證沒有內存碎片枣抱,接著回收原來那塊內存區(qū)域的垃圾對象熔吗,再次空出來一塊內存區(qū)域,兩塊內存區(qū)域重復循環(huán)使用
缺點:內存使用率太低佳晶,每次只使用其中一半
復制算法優(yōu)化
1個Eden區(qū) 80% 2個Survivor區(qū) 各10%
流程
1.剛開始對象分配都是在Eden區(qū)桅狠,如果Eden區(qū)快滿了,此時就會觸發(fā)垃圾回收轿秧。
2.此時就會把Eden區(qū)存活的對象一次性轉移到空著的S區(qū)垂攘,接著清空Eden區(qū),然后再次分配對象到Eden區(qū)
3.如果下次Eden又快滿了淤刃,再次觸發(fā)minor gc,就會把Eden區(qū)存活的對象和上一次Minor gc存活對象的S區(qū)的存活對象晒他,轉移到另一塊S區(qū)
優(yōu)點
1.只有10%的內存閑置了,90%的內存都使用上了
2.垃圾回收的性能和內存碎片的控制逸贾,還有內存使用的效率都非常好
標記清除
標記哪些對象是可以被垃圾回收的陨仅,直接對那塊內存區(qū)域的對象進行回收
缺點:存活對象再內存區(qū)域一個東一個西,產生大量的內存碎片铝侵,內存浪費
產生的問題
可能因為內存碎片太多的緣故灼伤,雖然所有的內存碎片加起來有很多的內存,但是這些內存都是零散的咪鲜,導致沒有完整的一塊內存區(qū)域來分配新的對象
標記整理 (針對老年代的)
1.首先標記出來老年代存活的對象狐赡,這些對象可能東一個西一個
2.把這個存活的對象再內存中移動,盡量挪到一邊去疟丙,讓存活對象緊湊的靠在一起颖侄,避免垃圾回收后產生內存碎片
系統(tǒng)對內存使用壓力的估算方法
1.鸟雏,每秒鐘系統(tǒng)會使用多少內存空間,然后多長時間會觸發(fā)一次垃圾回收览祖?
2.垃圾回收之后孝鹊,你們系統(tǒng)內大體會有多少對象存活下來?為什么展蒂?
3.然后都有哪些對象會存活下來又活?存活下來的對象會占多少內存空間?
如何進入老年代
1.躲過15次GC
對象每次再新生代里躲過一次GC被轉移到一塊S區(qū)锰悼,此時他的年齡就增長一歲柳骄,默認的設置下,當對象的年齡為15的時候就會進入老年代 jvm參數設置-XX:MaxTenuringThreshold
2.動態(tài)對象年齡判斷
就是當前放對象的S區(qū)箕般,一批對象的總大小大于了這塊S區(qū)域的內存大小的50%耐薯,那么此時大于等于這批對象的年齡的對象就進入老年代
規(guī)則:年齡1+年齡2+年齡n的多個年齡對象總和超過了Survivor區(qū)
域的50%,此時就會把年齡n以上的對象都放入老年代隘世。
一次達到Survivor 100%不會立馬觸發(fā)動態(tài)年齡判定機制,需要下一次GC的時候看你還是超過Survivor 50%鸠踪,才會進行動態(tài)年齡判定
實戰(zhàn):為了避免動態(tài)年齡判斷把S區(qū)的對象直接進入老年代丙者,如果新生代的內存有限,可以調整jvm參數"-
XX:SurvivorRatio=8" 默認Eden區(qū)80%营密,也可以降低Eden區(qū)的內存大小械媒,給S區(qū)更多的內存,然后讓每次Minor gc存活下來的對象進入S區(qū)
3.大對象直接進入老年代
jvm參數
是“-XX:PretenureSizeThreshold”评汰,可以把他的值設置為字節(jié)數纷捞,比如“1048576”字節(jié),就是1MB被去。如果你創(chuàng)建的對象大小大于這個值主儡,就直接進入老年代,根本不經過年輕代
好處:避免再新生代出現大對象惨缆,屢次躲過GC糜值,還得再S區(qū)來回復制多次才能進入老年代,
4.Minor GC后的對象太多無法放入Survivor區(qū)怎么辦坯墨?
假設Minor Gc后寂汇,eden區(qū)有150M的存活對象,S區(qū)放不下捣染,就直接進入老年代
5.老年代空間分配擔保規(guī)則
原因
minor gc之后骄瓣,S區(qū)放不下,有可能老年代也放不下
流程
再執(zhí)行minor gc之前耍攘,jvm都會檢查老年代的可用內存大小是否大于新生代所有的對象總大小榕栏。如果jvm參數設置了畔勤,就看看老年代的可用內存大小是否大于之前歷代每一次Minor gc后進入老年代的對象的平均大小。如果參數沒有設置臼膏,就會直接觸發(fā)一次full gc對老年代進行回收硼被,盡量回收一些內存,然后再執(zhí)行Minor gc
jvm參數設置
-XX:-HandlePromotionFailure”的參數是否設置了
對老年代觸發(fā)垃圾回收時機
1.再minor gc 之前渗磅,很可能Minor gc后要進入老年代的對象太多了嚷硫,老年代放不下,此時需要提前進行full gc,然后再進行minor gc
2.再minor gc之后始鱼,存活的對象老年代放不下了
3.老年代可以設置一個閾值仔掸,一旦老年代內存使用達到這閾值,就會觸發(fā)full gc
minor gc觸發(fā)時機
當新生代的Eden區(qū)和其中一個Survivor區(qū)空間不足時医清。
Minor gc后的幾種情況
1.存活對象 < S區(qū)的大小起暮,直接進入S區(qū)
2.存活對象 > S區(qū)的大小 但是 <老年代可用內存的大小, 進入老年代 - 存活對象 > S區(qū)的大小 同時 > 老年代可用內存大小会烙,此時老年代都放不下這些對象了负懦,就會出現 “Handle Promotion Failure 的情況,觸發(fā)一次full gc柏腻。full gc是對老年代回收纸厉,同時也一般回收新生代的垃圾,
4.如果再3的基礎上五嫂,老年代還是沒有足夠的內存空間存放Minor gc存活的對象颗品,就會發(fā)生OOM,內存溢出
垃圾回收器
Serial 和 Serial Old
Serial回收年輕代沃缘,Seraial Old回收老年代
原理:單線程運行躯枢,垃圾回收的時候會停止我們系統(tǒng)的其他線程的工作,讓我們的系統(tǒng)直接卡死槐臀,然后讓他們垃圾回收锄蹂,我們幾乎不用
ParNew
回收新生代,采用的是復制算法
流程:把Eden區(qū)的存活對象標記出來水慨,全部轉移到S1區(qū)败匹,然后一次性清空Eden區(qū)的垃圾對象,當Eden區(qū)再次滿的時候讥巡,觸發(fā)minor gc, 去標記Eden區(qū)和S1區(qū)的存活對象掀亩,一次性轉移到S2區(qū),然后清空Eden區(qū)和S1區(qū)欢顷。
jvm參數
-XX:+UseParNewGC
CMS
回收老年代槽棍,采用的是標記清除算法,會占用CPU資源
Concurrent Mode Failure問題
再并發(fā)清理階段,CMS只清理之前標記過的垃圾對象炼七。但是這個階段系統(tǒng)程序一直再運行缆巧,可能隨著系統(tǒng)運行有些對象進入老年代,同時還變成垃圾對象豌拙,這些垃圾對象就是浮動垃圾陕悬。雖然這些對象是垃圾對象,但CMS只能回收之前標記好的垃圾對象按傅,只能等到下一次GC的時候回收捉超。所以為了保證CMS再垃圾回收期間,還有預留內存空間讓一些對象進入老年代
jvm參數:“-XX:CMSInitiatingOccupancyFaction”參數可以用來設置老年代占用多少比例的時候觸發(fā)CMS垃圾回收唯绍,JDK 1.6里面默認的值是92%拼岳。老年代占用了92%空間了,就自動進行CMS垃圾回收况芒,預留8%的空間給并發(fā)回收期間惜纸,系統(tǒng)程序把一些新對象放入老年代中。
如果再CMS垃圾回收期間绝骚,系統(tǒng)要放入的對象大于老年代可用內存耐版,會發(fā)生concurrent mode failure問題,就是說并發(fā)垃圾回收失敗了压汪,我一邊清理 你一邊把對象放入老年代粪牲,內存都不夠了。此時就會用Serial Old代替CMS蛾魄,直接強行把系統(tǒng)程序 stop the world,重新進行長時間的Gc Roots追蹤虑瀑,標記全部的垃圾對象湿滓,不允許創(chuàng)建新對象滴须,然后一次性回收垃圾對象,恢復系統(tǒng)運行
內存碎片問題
老年代的CMS采用的是標記-清除算法叽奥,每次都是標記出來垃圾對象扔水,然后一次性回收。這樣會導致大量的內存碎片朝氓。如果內存碎片太多魔市,會導致后續(xù)進入老年代的對象找不到可用的連續(xù)內存空間,然后觸發(fā)full gc赵哲。CMS有一個參數是“-XX:+UseCMSCompactAtFullCollection”待德,默認就打開了。再full gc后需要再次進行 stop the world.停止工作線程枫夺,進行內存碎片整理将宪。把存活的對象挪到一邊,空出來大片的連續(xù)內存,避免內存碎片较坛。還有一個參數是“-XX:CMSFullGCsBeforeCompaction”印蔗,這個意思是執(zhí)行多少次Full GC之后再執(zhí)行一次內存碎片整理的工作,默認
是0丑勤,意思就是每次Full GC之后都會進行一次內存整理华嘹。
流程
1.初始標記
標記出來被GC ROOTS直接引用的對象,會造成stop the world, 但其實影響不大法竞, 因為速度很快
2.并發(fā)標記
可以讓系統(tǒng)線程隨意創(chuàng)建新的對象耙厚,繼續(xù)運行,再運行期間可能創(chuàng)建新的存活對象爪喘,也可能部分存活的對象失去引用颜曾,變成垃圾對象。這個階段是對老年代所有對象進行Gc roots跟蹤秉剑,是最耗時的泛豪,但是跟系統(tǒng)并發(fā)運行的,不會對系統(tǒng)造成影響
3.重新標記
再第第二階段侦鹏,你一邊標記存活對象和垃圾對象诡曙,一邊系統(tǒng)再不停的創(chuàng)建新的對象,讓老對象變成垃圾對象略水。再第二階段結束后价卤,肯定有存活對象和垃圾對象是第二階段沒有標記出來的。所以此時進入第三階段渊涝,要繼續(xù)讓系統(tǒng)程序停止運行慎璧,再次進入stop the world.然后重新標記再第二階段創(chuàng)建的對象還有一些已有對象可能失去引用變成垃圾的。運行速度很快
4.并發(fā)清理
這個階段系統(tǒng)程序可以隨意運行跨释,清理標記的垃圾對象胸私。這個階段比較耗時,因為需要進行對象的清理鳖谈,但是和系統(tǒng)程序并發(fā)運行的岁疼,不影響系統(tǒng)程序
G1
可以同時回收新生代和老年代,算法性能更好 缆娃, 特點就是把java堆內存拆分成多個大小相等的region,最大的特點就是設置垃圾回收預期停頓時間捷绒。其實我們對內存合理分配和參數優(yōu)化就是為了減少minor gc 和full gc,盡量減少gc帶來的系統(tǒng)停頓,避免影響系統(tǒng)請求
parNew和cms的痛點:stop the world
默認新生代最多只能占據堆內存60%的Region
GC時機
根據你設定的gc停頓時間給你的新生代不停分配更多Region然后到一定程度贯要,感覺差不多了暖侨,就會觸發(fā)新生代gc,保證新生代gc的時候導致的系統(tǒng)停頓時間在你預設范圍內崇渗。
在G1中字逗,大對象的判定規(guī)則就是一個大對象超過了一個Region大小的50%函荣,比如按照上面算的,每個Region是2MB扳肛,只要一個大對象超過了1MB傻挂,就會被放入大對象專門的Region中。而且一個大對象如果太大挖息,可能會橫跨多個Region來存放
核心設計思路
G1可以設定垃圾回收對系統(tǒng)的影響金拒,他自己通過把內存拆分成大量小region,以及追蹤每個region可以回收的對象大小和預估時間套腹,最后再垃圾回收的時候绪抛,盡量把gc對系統(tǒng)的影響控制再你指定的時間范圍內,同時再有限的時間內回收更多的垃圾
流程
1.初始標記
首先觸發(fā)一個初始標記操作电禀,這個過程是需要 stop the world的幢码,僅僅只是標記一下gc roots能直接引用的對象,速度很快
2.并發(fā)標記
這個階段系統(tǒng)程序可以運行尖飞,同時進行gc roots跟蹤所有存活對象
3.最終標記
這個階段系統(tǒng)程序再次進入stop the world症副,會根據并發(fā)標記階段記錄了那些對象修改,最終標記哪些是存活對象政基,哪些是垃圾對象
4.混合回收
因為我們設定了對GC停頓時間的目標贞铣,所以說他會從新生代、老年代沮明、大對象里各自挑選一些Region辕坝,保證用指定
的時間(比如200ms)回收盡可能多的垃圾,這就是所謂的混合回收荐健,
Stop the World問題
再垃圾回收的時候酱畅,盡可能的讓垃圾回收器專心致志的工作,不能隨便讓我們的系統(tǒng)再創(chuàng)建新的對象江场,所以此時jvm會再后臺進入 stop the world 狀態(tài)纺酸,這樣的話我們系統(tǒng)暫停運行,不再創(chuàng)建新的對象扛稽,同時就是讓垃圾回收線程盡快完成垃圾回收工作吁峻,一旦垃圾回收完畢滑负,就可以繼續(xù)恢復我們系統(tǒng)的運行了
導入文件(opml在张、pos格式)
另存為圖片格式(.png)
另存為opml格式(可用于導入)