十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
本篇內(nèi)容介紹了“什么是GC和GC Tuning”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:域名注冊、虛擬主機、營銷軟件、網(wǎng)站建設、巴林左旗網(wǎng)站維護、網(wǎng)站推廣。
C語言申請內(nèi)存:malloc free
C++: new delete
c/C++ 手動回收內(nèi)存
Java: new ?
自動內(nèi)存回收,編程上簡單,系統(tǒng)不容易出錯,手動釋放內(nèi)存,容易出兩種類型的問題:
忘記回收
多次回收
沒有任何引用指向的一個對象或者多個對象(循環(huán)引用)
引用計數(shù)(ReferenceCount)
根可達算法(RootSearching)
標記清除(mark sweep) - 位置不連續(xù) 產(chǎn)生碎片 效率偏低(兩遍掃描)
拷貝算法 (copying) - 沒有碎片,浪費空間
標記壓縮(mark compact) - 沒有碎片,效率偏低(兩遍掃描,指針需要調(diào)整)
部分垃圾回收器使用的模型
除Epsilon ZGC Shenandoah之外的GC都是使用邏輯分代模型
G1是邏輯分代,物理不分代
除此之外不僅邏輯分代,而且物理分代
新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元數(shù)據(jù)區(qū)(1.8) Metaspace
永久代 元數(shù)據(jù) - Class
永久代必須指定大小限制 ,元數(shù)據(jù)可以設置,也可以不設置,無上限(受限于物理內(nèi)存)
字符串常量 1.7 - 永久代,1.8 - 堆
MethodArea邏輯概念 - 永久代、元數(shù)據(jù)
新生代 = Eden + 2個suvivor區(qū)
YGC回收之后,大多數(shù)的對象會被回收,活著的進入s0
再次YGC,活著的對象eden + s0 -> s1
再次YGC,eden + s1 -> s0
年齡足夠 -> 老年代 (15 CMS 6)
s區(qū)裝不下 -> 老年代
老年代
頑固分子
老年代滿了FGC Full GC
GC Tuning (Generation)
盡量減少FGC
MinorGC = YGC
MajorGC = FGC
對象分配過程圖
動態(tài)年齡:(不重要) https://www.jianshu.com/p/989d3b06a49d
分配擔保:(不重要) YGC期間 survivor區(qū)空間不夠了 空間擔保直接進入老年代 參考:https://cloud.tencent.com/developer/article/1082730
JDK誕生 Serial追隨 提高效率,誕生了PS,為了配合CMS,誕生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它開啟了并發(fā)回收的過程,但是CMS毛病較多,因此目前任何一個JDK版本默認是CMS 并發(fā)垃圾回收是因為無法忍受STW
Serial 年輕代 串行回收
PS 年輕代 并行回收
ParNew 年輕代 配合CMS的并行回收
SerialOld
ParallelOld
ConcurrentMarkSweep 老年代 并發(fā)的, 垃圾回收和應用程序同時運行,降低STW的時間(200ms) CMS問題比較多,所以現(xiàn)在沒有一個版本默認是CMS,只能手工指定 CMS既然是MarkSweep,就一定會有碎片化的問題,碎片到達一定程度,CMS的老年代分配對象分配不下的時候,使用SerialOld 進行老年代回收 想象一下: PS + PO -> 加內(nèi)存 換垃圾回收器 -> PN + CMS + SerialOld(幾個小時 - 幾天的STW) 幾十個G的內(nèi)存,單線程回收 -> G1 + FGC 幾十個G -> 上T內(nèi)存的服務器 ZGC 算法:三色標記 + Incremental Update
G1(10ms) 算法:三色標記 + SATB
ZGC (1ms) PK C++ 算法:ColoredPointers + LoadBarrier
Shenandoah 算法:ColoredPointers + WriteBarrier
Eplison
PS 和 PN區(qū)別的延伸閱讀:點擊
垃圾收集器跟內(nèi)存大小的關(guān)系
Serial 幾十兆
PS 上百兆 - 幾個G
CMS - 20G
G1 - 上百G
ZGC - 4T - 16T(JDK13)
1.8默認的垃圾回收:PS + ParallelOld
-XX:+UseSerialGC
= Serial New (DefNew) + Serial Old
小型程序。默認情況下不會是這種選項,HotSpot會根據(jù)計算及配置和JDK版本自動選擇收集器
-XX:+UseParNewGC
= ParNew + SerialOld
這個組合已經(jīng)很少用(在某些版本中已經(jīng)廢棄)
https://stackoverflow.com/questions/34962257/why-remove-support-for-parnewserialold-anddefnewcms-in-the-future
-XX:+UseConcMarkSweepGC
= ParNew + CMS + Serial Old
-XX:+UseParallelGC
= Parallel Scavenge + Parallel Old (1.8默認) 【PS + SerialOld】
-XX:+UseParallelOldGC
= Parallel Scavenge + Parallel Old
-XX:+UseG1GC
= G1
Linux中沒找到默認GC的查看方法,而windows中會打印UseParallelGC
java +XX:+PrintCommandLineFlags -version
通過GC的日志來分辨
Linux下1.8版本默認的垃圾回收器到底是什么?
1.8.0_181 默認(看不出來)Copy MarkCompact
1.8.0_222 默認 PS + PO
JVM的命令行參數(shù)參考
HotSpot參數(shù)分類
標準: - 開頭,所有的HotSpot都支持 非標準:-X 開頭,特定版本HotSpot支持特定命令 不穩(wěn)定:-XX 開頭,下個版本可能取消
java -version
java -X 試驗用程序:
import java.util.List; import java.util.LinkedList; public class HelloGC { public static void main(String[] args) { System.out.println("HelloGC!"); List list = new LinkedList(); for(;;) { byte[] b = new byte[1024*1024]; list.add(b); } } }
區(qū)分概念:內(nèi)存泄漏memory leak,內(nèi)存溢出out of memory
java -XX:+PrintCommandLineFlags HelloGC
java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC PrintGCDetails PrintGCTimeStamps PrintGCCauses
java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC
java -XX:+PrintFlagsInitial
默認參數(shù)值
java -XX:+PrintFlagsFinal
最終參數(shù)值
java -XX:+PrintFlagsFinal | grep xxx
找到對應的參數(shù)
java -XX:+PrintFlagsFinal -version |grep GC
每種垃圾回收器的日志格式是不同的!
PS日志格式
heap dump部分:
eden space 5632K, 94% used [0x00000000ff980000,0x00000000ffeb3e28,0x00000000fff00000) 后面的內(nèi)存地址指的是,起始地址,使用空間結(jié)束地址,整體空間結(jié)束地址
total = eden + 1個survivor(原因是survivor是復制算法,實際可用空間就只能是1個survivor)
吞吐量:用戶代碼時間 /(用戶代碼執(zhí)行時間 + 垃圾回收時間)
響應時間:STW越短,響應時間越好
所謂調(diào)優(yōu),首先確定,追求啥?吞吐量優(yōu)先,還是響應時間優(yōu)先?還是在滿足一定的響應時間的情況下,要求達到多大的吞吐量...
問題: 科學計算,吞吐量。數(shù)據(jù)挖掘,thrput。吞吐量優(yōu)先的一般:(PS + PO) 響應時間:網(wǎng)站 GUI API (1.8 G1)
根據(jù)需求進行JVM規(guī)劃和預調(diào)優(yōu)
優(yōu)化運行JVM運行環(huán)境(慢,卡頓)
解決JVM運行過程中出現(xiàn)的各種問題(OOM)
調(diào)優(yōu),從業(yè)務場景開始,沒有業(yè)務場景的調(diào)優(yōu)都是耍流氓
無監(jiān)控(壓力測試,能看到結(jié)果),不調(diào)優(yōu)
步驟:
-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
或者每天產(chǎn)生一個日志文件
響應時間、停頓時間 [CMS G1 ZGC] (需要給用戶作響應)
吞吐量 = 用戶時間 /( 用戶時間 + GC時間) [PS]
熟悉業(yè)務場景(沒有最好的垃圾回收器,只有最合適的垃圾回收器)
選擇回收器組合
計算內(nèi)存需求(經(jīng)驗值 1.5G 16G)
選定CPU(越高越好)
設定年代大小、升級年齡
設定日志參數(shù)
觀察日志情況
案例1:垂直電商,最高每日百萬訂單,處理訂單系統(tǒng)需要什么樣的服務器配置?
這個問題比較業(yè)余,因為很多不同的服務器配置都能支撐(1.5G 16G)
1小時360000集中時間段, 100個訂單/秒,(找一小時內(nèi)的高峰期,1000訂單/秒)
經(jīng)驗值,
非要計算:一個訂單產(chǎn)生需要多少內(nèi)存?512K * 1000 500M內(nèi)存
專業(yè)一點兒問法:要求響應時間100ms
壓測!
案例2:12306遭遇春節(jié)大規(guī)模搶票應該如何支撐?
12306應該是中國并發(fā)量最大的秒殺網(wǎng)站:
號稱并發(fā)量100W最高
cdn -> LVS -> NGINX -> 業(yè)務系統(tǒng) -> 每臺機器1W并發(fā)(10K問題) 100臺機器
普通電商訂單 -> 下單 ->訂單系統(tǒng)(IO)減庫存 ->等待用戶付款
12306的一種可能的模型: 下單 -> 減庫存 和 訂單(redis kafka) 同時異步進行 ->等付款
減庫存最后還會把壓力壓到一臺服務器
可以做分布式本地庫存 + 單獨服務器做庫存均衡
大流量的處理方法:分而治之
怎么得到一個事務會消耗多少內(nèi)存?
弄臺機器,看能承受多少TPS?是不是達到目標?擴容或調(diào)優(yōu),讓它達到
用壓測來確定
有一個50萬PV的資料類網(wǎng)站(從磁盤提取文檔到內(nèi)存)原服務器32位,1.5G 的堆,用戶反饋網(wǎng)站比較緩慢,因此公司決定升級,新的服務器為64位,16G 的堆內(nèi)存,結(jié)果用戶反饋卡頓十分嚴重,反而比以前效率更低了
為什么原網(wǎng)站慢? 很多用戶瀏覽數(shù)據(jù),很多數(shù)據(jù)load到內(nèi)存,內(nèi)存不足,頻繁GC,STW長,響應時間變慢
為什么會更卡頓? 內(nèi)存越大,F(xiàn)GC時間越長
咋辦? PS -> PN + CMS 或者 G1
系統(tǒng)CPU經(jīng)常100%,如何調(diào)優(yōu)?(面試高頻) CPU100%那么一定有線程在占用系統(tǒng)資源,
找出哪個進程cpu高(top)
該進程中的哪個線程cpu高(top -Hp)
導出該線程的堆棧 (jstack)
查找哪個方法(棧幀)消耗時間 (jstack)
工作線程占比高 | 垃圾回收線程占比高
系統(tǒng)內(nèi)存飆高,如何查找問題?(面試高頻)
導出堆內(nèi)存 (jmap)
分析 (jhat jvisualvm mat jprofiler ... )
如何監(jiān)控JVM
jstat jvisualvm jprofiler arthas top...
測試代碼:
package com.test.jvm.gc; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 從數(shù)據(jù)庫中讀取信用數(shù)據(jù),套用模型,并把結(jié)果進行記錄和傳輸 */ public class TestFullGCProblem01 { private static class CardInfo { BigDecimal price = new BigDecimal(0.0); String name = "張三"; int age = 5; Date birthDate = new Date(); public void m() {} } private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50, new ThreadPoolExecutor.DiscardOldestPolicy()); public static void main(String[] args) throws Exception { executor.setMaximumPoolSize(50); for (;;){ modelFit(); Thread.sleep(100); } } private static void modelFit(){ ListtaskList = getAllCardInfo(); taskList.forEach(info -> { // do something executor.scheduleWithFixedDelay(() -> { //do sth with info info.m(); }, 2, 3, TimeUnit.SECONDS); }); } private static List getAllCardInfo(){ List taskList = new ArrayList<>(); for (int i = 0; i < 100; i++) { CardInfo ci = new CardInfo(); taskList.add(ci); } return taskList; } }
java -Xms200M -Xmx200M -XX:+PrintGC com.test.jvm.gc.TestFullGCProblem01
一般是運維團隊首先收到報警信息(CPU Memory)
top
命令觀察到問題:內(nèi)存不斷增長 CPU占用率居高不下
top -Hp
觀察進程中的線程,哪個線程CPU和內(nèi)存占比高
jps
定位具體java進程 jstack
定位線程狀況,重點關(guān)注:WAITING BLOCKED eg. waiting on <0x0000000088ca3310> (a java.lang.Object)
假如有一個進程中100個線程,很多線程都在waiting on
為什么阿里規(guī)范里規(guī)定,線程的名稱(尤其是線程池)都要寫有意義的名稱。怎么樣自定義線程池里的線程名稱?(自定義ThreadFactory)
jinfo pid
jstat -gc
動態(tài)觀察gc情況 / 閱讀GC日志發(fā)現(xiàn)頻繁GC / arthas觀察 / jconsole/jvisualVM/ Jprofiler(最好用) jstat -gc 4655 500 : 每個500個毫秒打印GC的情況 如果面試官問你是怎么定位OOM問題的?如果你回答用圖形界面(錯誤) 1:已經(jīng)上線的系統(tǒng)不用圖形界面用什么?(cmdline arthas) 2:圖形界面到底用在什么地方?測試!測試的時候進行監(jiān)控?。▔簻y觀察)
jmap -histo 4655 | head -20
,查找有多少對象產(chǎn)生
jmap -dump:format=b,file=xxx pid
: 線上系統(tǒng),內(nèi)存特別大,jmap執(zhí)行期間會對進程產(chǎn)生很大影響,甚至卡頓(電商不適合) 1:設定了參數(shù)HeapDump,OOM的時候會自動產(chǎn)生堆轉(zhuǎn)儲文件 2:很多服務器備份(高可用),停掉這臺服務器對其他服務器不影響 3:在線定位(一般小點兒公司用不到)
java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.test.jvm.gc.TestFullGCProblem01
使用MAT / jhat /jvisualvm 進行dump文件分析:https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html jhat -J-mx512M xxx.dump
http://192.168.17.11:7000 拉到最后:找到對應鏈接 可以使用OQL查找特定問題對象
找到代碼的問題
程序啟動加入?yún)?shù):
java -Djava.rmi.server.hostname=192.168.17.11 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11111 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false XXX
如果遭遇 Local host name unknown:XXX的錯誤,修改/etc/hosts文件,把XXX加入進去
192.168.17.11 basic localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
關(guān)閉linux防火墻(實戰(zhàn)中應該打開對應端口)
service iptables stop chkconfig iptables off #永久關(guān)閉
windows上打開 jconsole遠程連接 192.168.17.11:11111
https://www.cnblogs.com/liugh/p/7620336.html (簡單做法)
為什么需要在線排查? 在生產(chǎn)上我們經(jīng)常會碰到一些不好排查的問題,例如線程安全問題,用最簡單的threaddump或者heapdump不好查到問題原因。為了排查這些問題,有時我們會臨時加一些日志,比如在一些關(guān)鍵的函數(shù)里打印出入?yún)?,然后重新打包發(fā)布,如果打了日志還是沒找到問題,繼續(xù)加日志,重新打包發(fā)布。對于上線流程復雜而且審核比較嚴的公司,從改代碼到上線需要層層的流轉(zhuǎn),會大大影響問題排查的進度。
jvm
觀察jvm信息
thread
定位線程問題
dashboard
觀察系統(tǒng)情況
heapdump
+ jhat
分析
jad
反編譯 動態(tài)代理生成類的問題定位 第三方的類(觀察代碼) 版本問題(確定自己最新提交的版本是不是被使用)
redefine
熱替換 目前有些限制條件:只能改方法實現(xiàn)(方法已經(jīng)運行完成),不能改方法名, 不能改屬性 m() -> mm()
sc
- search class
watch
- watch method
沒有包含的功能:jmap
由于做YGC時,需要掃描整個OLD區(qū)(原因是old區(qū)可能由指向young區(qū)的東西),效率非常低,所以JVM設計了CardTable,如果一個OLD區(qū)CardTable中有對象指向Y區(qū),就將它設為Dirty,下次掃描時,只需要掃描Dirty Card。在結(jié)構(gòu)上,Card Table用BitMap來實現(xiàn)。
難點:在標記對象過程中,對象引用關(guān)系正在發(fā)生改變
白色:未被標記的對象
灰色:自身被標記,成員變量未被標記
黑色:自身和成員變量均已標記完成
graph TD A(A:黑色) --> B(B:灰色) A(A:黑色) --> C(C:灰色) A(A:黑色) --> |新增| D(D:白色) B --> |刪除| D(D:白色)
漏標(2種情況):漏標是指,本來時live object,但是由于沒有遍歷到,被當成垃圾回收了。
在remakr過程中,黑色指向了白色,如果不對黑色重新掃描,則會漏標,會把白色對象當做沒有新引用指向從而回收掉。
并發(fā)標記過程中,刪除了所有從灰色到白色的引用,會產(chǎn)生漏標,此時白色對象應該被回收。
怎么解決(2個方式):
incremental update:增量更新,關(guān)注引用的增加,把黑色重新標記為灰色,下次重新掃描屬性。(CMS使用了這種)
SATB(snapshot at the beginning):關(guān)注引用的刪除,當指向消失時,要把這個引用推到GC的堆棧,保證白色還能被GC掃描到。(G1使用了這種方式)
Concurrent Mark Sweep
a mostly concurrent, low-pause collector
4 phases
initial mark
concurrent mark
remark
concurrent sweep
這個過程會發(fā)生STW,但是由于找的對象會比較少,因此STW時間會很短
這個過程不會發(fā)送STW
為了解決并發(fā)標記過程中,某些對象的引用不存在了,所以需要重新標記
這個過程會發(fā)生STW
并發(fā)清理的過程還是可能會產(chǎn)生垃圾,這部分稱為浮動垃圾,會在下次GC的時候清理
Memory Fragmentation
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction 默認為0 指的是經(jīng)過多少次FGC才進行壓縮
Floating Garbage
Concurrent Mode Failure 產(chǎn)生:if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation, then theapplication is paused and the collection is completed with all the applicationthreads stopped
解決方案:降低觸發(fā)CMS的閾值
PromotionFailed
解決方案類似,保持老年代有足夠的空間
–XX:CMSInitiatingOccupancyFraction 92%
可以降低這個值,讓CMS保持老年代足夠的空間
執(zhí)行命令:java -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.test.jvm.gc.TestFullGCProblem01
[GC (Allocation Failure) [ParNew: 6144K->640K(6144K), 0.0265885 secs] 6585K->2770K(19840K), 0.0268035 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
ParNew:年輕代收集器
6144->640:收集前后的對比
(6144):整個年輕代容量
6585 -> 2770:整個堆的情況
(19840):整個堆大小
[GC (CMS Initial Mark) [1 CMS-initial-mark: 8511K(13696K)] 9866K(19840K), 0.0040321 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] // 8511 (13696) : 老年代使用(最大) // 9866 (19840) : 整個堆使用(最大) [CMS-concurrent-mark-start] [CMS-concurrent-mark: 0.018/0.018 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] // 這里的時間意義不大,因為是并發(fā)執(zhí)行 [CMS-concurrent-preclean-start] [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] // 標記Card為Dirty,也稱為Card Marking [GC (CMS Final Remark) [YG occupancy: 1597 K (6144 K)][Rescan (parallel) , 0.0008396 secs][weak refs processing, 0.0000138 secs][class unloading, 0.0005404 secs][scrub symbol table, 0.0006169 secs][scrub string table, 0.0004903 secs][1 CMS-remark: 8511K(13696K)] 10108K(19840K), 0.0039567 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] // STW階段,YG occupancy:年輕代占用及容量 // [Rescan (parallel):STW下的存活對象標記 // weak refs processing: 弱引用處理 // class unloading: 卸載用不到的class // scrub symbol(string) table: // cleaning up symbol and string tables which hold class-level metadata and // internalized string respectively // CMS-remark: 8511K(13696K): 階段過后的老年代占用及容量 // 10108K(19840K): 階段過后的堆占用及容量 [CMS-concurrent-sweep-start] [CMS-concurrent-sweep: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] // 標記已經(jīng)完成,進行并發(fā)清理 [CMS-concurrent-reset-start] [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] // 重置內(nèi)部結(jié)構(gòu),為下次GC做準備
官方簡介
G1是一種服務端應用使用的垃圾收集器,目標是在多核、大內(nèi)存的機器上,它在大多數(shù)情況下可以實現(xiàn)指定GC暫停時間,同時還能保持較高的吞吐量。
與PS相比較,G1大約比其低10%~15%的吞吐量,但是維持響應時間在200ms。
G1使用分治的思想來管理內(nèi)存,把內(nèi)存分割成一個一個的Region。每個Region可能是某一個代,例如old、survivor、tenured、Humongous。每個區(qū)域不是固定的。
特點
并發(fā)收集
壓縮空閑空間不會延長GC的暫停時間
更易預測的GC暫停時間
適用不需要實現(xiàn)很高吞吐量的場景
基本概念
CSet = CollectionSet 一組可被回收的分區(qū)的集合。在CSet中存活的數(shù)據(jù)會在GC過程中被移動到另一個可用分區(qū),CSet中的分區(qū)可以來自Eden空間、Survivor空間或者老年代。CSet會占用不到整個堆空間的1%大小。
RSet = RememberedSet ** 記錄了其他region中的對象到本region的引用。其價值在于:垃圾收集器不需要掃描整個堆找到誰引用了當前分區(qū)中的對象,只需要掃描RSet即可。RSet是維護在每個Region中的。 ** 由于RSet的存在,那么每次給對象賦引用的時候,就得做一些額外的操作,指的時在RSet中做一些額外的記錄(在GC中被稱為寫屏障)。
[GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0015790 secs] // young -> 年輕代 Evacuation-> 復制存活對象 // initial-mark 混合回收的階段,這里是YGC混合老年代回收 [Parallel Time: 1.5 ms, GC Workers: 1] // 一個GC線程 [GC Worker Start (ms): 92635.7] [Ext Root Scanning (ms): 1.1] [Update RS (ms): 0.0] [Processed Buffers: 1] [Scan RS (ms): 0.0] [Code Root Scanning (ms): 0.0] [Object Copy (ms): 0.1] [Termination (ms): 0.0] [Termination Attempts: 1] [GC Worker Other (ms): 0.0] [GC Worker Total (ms): 1.2] [GC Worker End (ms): 92636.9] [Code Root Fixup: 0.0 ms] [Code Root Purge: 0.0 ms] [Clear CT: 0.0 ms] [Other: 0.1 ms] [Choose CSet: 0.0 ms] [Ref Proc: 0.0 ms] [Ref Enq: 0.0 ms] [Redirty Cards: 0.0 ms] [Humongous Register: 0.0 ms] [Humongous Reclaim: 0.0 ms] [Free CSet: 0.0 ms] [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)] [Times: user=0.00 sys=0.00, real=0.00 secs] // 以下是混合回收其他階段 [GC concurrent-root-region-scan-start] [GC concurrent-root-region-scan-end, 0.0000078 secs] [GC concurrent-mark-start] // 無法evacuation,進行FGC [Full GC (Allocation Failure) 18M->18M(20M), 0.0719656 secs] [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)], [Metaspace: 38 76K->3876K(1056768K)] [Times: user=0.07 sys=0.00, real=0.07 secs]
OOM產(chǎn)生的原因多種多樣,有些程序未必產(chǎn)生OOM,不斷FGC(CPU飆高,但內(nèi)存回收特別少) (上面案例)
硬件升級系統(tǒng)反而卡頓的問題(見上)
線程池不當運用產(chǎn)生OOM問題(見上) 不斷的往List里加對象(實在太LOW)
jira問題 實際系統(tǒng)不斷重啟 解決問題 加內(nèi)存 + 更換垃圾回收器 G1 真正問題在哪兒?不知道
tomcat http-header-size過大問題(Hector)
lambda表達式導致方法區(qū)溢出問題(MethodArea / Perm Metaspace) LambdaGC.java -XX:MaxMetaspaceSize=9M -XX:+PrintGCDetails
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" -XX:MaxMetaspaceSize=9M -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.1\lib\idea_rt.jar=49316:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\DNSns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\work\ijprojects\JVM\out\production\JVM;C:\work\ijprojects\ObjectSize\out\artifacts\ObjectSize_jar\ObjectSize.jar" com.mashibing.jvm.gc.LambdaGC [GC (Metadata GC Threshold) [PSYoungGen: 11341K->1880K(38400K)] 11341K->1888K(125952K), 0.0022190 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Metadata GC Threshold) [PSYoungGen: 1880K->0K(38400K)] [ParOldGen: 8K->1777K(35328K)] 1888K->1777K(73728K), [Metaspace: 8164K->8164K(1056768K)], 0.0100681 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] [GC (Last ditch collection) [PSYoungGen: 0K->0K(38400K)] 1777K->1777K(73728K), 0.0005698 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Last ditch collection) [PSYoungGen: 0K->0K(38400K)] [ParOldGen: 1777K->1629K(67584K)] 1777K->1629K(105984K), [Metaspace: 8164K->8156K(1056768K)], 0.0124299 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:388) at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:411) Caused by: java.lang.OutOfMemoryError: Compressed class space at sun.misc.Unsafe.defineClass(Native Method) at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:63) at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:399) at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:394) at java.security.AccessController.doPrivileged(Native Method) at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:393) at sun.reflect.MethodAccessorGenerator.generateSerializationConstructor(MethodAccessorGenerator.java:112) at sun.reflect.ReflectionFactory.generateConstructor(ReflectionFactory.java:398) at sun.reflect.ReflectionFactory.newConstructorForSerialization(ReflectionFactory.java:360) at java.io.ObjectStreamClass.getSerializableConstructor(ObjectStreamClass.java:1574) at java.io.ObjectStreamClass.access$1500(ObjectStreamClass.java:79) at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:519) at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:494) at java.security.AccessController.doPrivileged(Native Method) at java.io.ObjectStreamClass.(ObjectStreamClass.java:494) at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:391) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1134) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at javax.management.remote.rmi.RMIConnectorServer.encodeJRMPStub(RMIConnectorServer.java:727) at javax.management.remote.rmi.RMIConnectorServer.encodeStub(RMIConnectorServer.java:719) at javax.management.remote.rmi.RMIConnectorServer.encodeStubInAddress(RMIConnectorServer.java:690) at javax.management.remote.rmi.RMIConnectorServer.start(RMIConnectorServer.java:439) at sun.management.jmxremote.ConnectorBootstrap.startLocalConnectorServer(ConnectorBootstrap.java:550) at sun.management.Agent.startLocalManagementAgent(Agent.java:137)
直接內(nèi)存溢出問題(少見) 《深入理解Java虛擬機》P59,使用Unsafe分配直接內(nèi)存,或者使用NIO的問題
棧溢出問題 -Xss設定太小
比較一下這兩段程序的異同,分析哪一個是更優(yōu)的寫法:
Object o = null; for(int i=0; i<100; i++) { o = new Object(); //業(yè)務處理 }
for(int i=0; i<100; i++) { Object o = new Object(); }
重寫finalize引發(fā)頻繁GC 小米云,HBase同步系統(tǒng),系統(tǒng)通過nginx訪問超時報警,最后排查,C++程序員重寫finalize引發(fā)頻繁GC問題 為什么C++程序員會重寫finalize?(new delete) finalize耗時比較長(200ms)
如果有一個系統(tǒng),內(nèi)存一直消耗不超過10%,但是觀察GC日志,發(fā)現(xiàn)FGC總是頻繁產(chǎn)生,會是什么引起的? System.gc() (這個比較Low)
Distuptor有個可以設置鏈的長度,如果過大,然后對象大,消費完不主動釋放,會溢出 (來自 死物風情)
用jvm都會溢出,mycat用崩過,1.6.5某個臨時版本解析sql子查詢算法有問題,9個exists的聯(lián)合sql就導致生成幾百萬的對象(來自 死物風情)
new 大量線程,會產(chǎn)生 native thread OOM,(low)應該用線程池, 解決方案:減少堆空間(太TMlow了),預留更多內(nèi)存產(chǎn)生native thread JVM內(nèi)存占物理內(nèi)存比例 50% - 80%
-Xmn -Xms -Xmx -Xss
年輕代 最小堆 最大堆 ??臻g
-XX:+UseTLAB
使用TLAB,默認打開
-XX:+PrintTLAB
打印TLAB的使用情況
-XX:TLABSize
設置TLAB大小
-XX:+DisableExplictGC
System.gc()不管用 ,F(xiàn)GC
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintHeapAtGC
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationConcurrentTime
(低) 打印應用程序時間
-XX:+PrintGCApplicationStoppedTime
(低) 打印暫停時長
-XX:+PrintReferenceGC
(重要性低) 記錄回收了多少種不同引用類型的引用
-verbose:class
類加載詳細過程
-XX:+PrintVMOptions
-XX:+PrintFlagsFinal
和-XX:+PrintFlagsInitial
必須會用
-Xloggc:opt/log/gc.log
-XX:MaxTenuringThreshold
升代年齡,最大值15
鎖自旋次數(shù) -XX:PreBlockSpin
熱點代碼檢測參數(shù)-XX:CompileThreshold
逃逸分析 標量替換 ... 這些不建議設置
-XX:SurvivorRatio
-XX:PreTenureSizeThreshold
大對象到底多大
-XX:MaxTenuringThreshold
-XX:+ParallelGCThreads
并行收集器的線程數(shù),同樣適用于CMS,一般設為和CPU核數(shù)相同
-XX:+UseAdaptiveSizePolicy
自動選擇各區(qū)大小比例
-XX:+UseConcMarkSweepGC
-XX:ParallelCMSThreads
CMS線程數(shù)量
-XX:CMSInitiatingOccupancyFraction
使用多少比例的老年代后開始CMS收集,默認是68%(近似值),如果頻繁發(fā)生SerialOld卡頓,應該調(diào)小,(頻繁CMS回收)
-XX:+UseCMSCompactAtFullCollection
在FGC時進行壓縮
-XX:CMSFullGCsBeforeCompaction
多少次FGC之后進行壓縮
-XX:+CMSClassUnloadingEnabled
-XX:CMSInitiatingPermOccupancyFraction
達到什么比例時進行Perm回收
GCTimeRatio
設置GC時間占用程序運行時間的百分比
-XX:MaxGCPauseMillis
停頓時間,是一個建議時間,GC會嘗試用各種手段達到這個時間,比如減小年輕代
-XX:+UseG1GC
-XX:MaxGCPauseMillis
建議值,G1會嘗試調(diào)整Young區(qū)的塊數(shù)來達到這個值
-XX:GCPauseIntervalMillis
?GC的間隔時間
-XX:+G1HeapRegionSize
分區(qū)大小,建議逐漸增大該值,1 2 4 8 16 32。 隨著size增加,垃圾的存活時間更長,GC間隔更長,但每次GC的時間也會更長 ZGC做了改進(動態(tài)區(qū)塊大小)
G1NewSizePercent
新生代最小比例,默認為5%
G1MaxNewSizePercent
新生代最大比例,默認為60%
GCTimeRatio
GC時間建議比例,G1會根據(jù)這個值調(diào)整堆空間
ConcGCThreads
線程數(shù)量
InitiatingHeapOccupancyPercent
啟動G1的堆空間占用比例
“什么是GC和GC Tuning”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!