自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一次由groovy引起的fullGC問題排查

開發(fā) 后端
綜上分析,groovy 錯誤的使用方式導(dǎo)致 class 對象常駐堆外內(nèi)存且隨著調(diào)用頻率增長。

一、問題背景

二、分析過程

  • 2.1 參數(shù)配置
  • 2.2 定位過程
  • 2.3 JVM分析
  • 2.4 問題分析

三、解決方案

一、問題背景

prometheus監(jiān)控報警生效后,某服務(wù)每天的上午 8-12 點(diǎn)間會有fullGC的報警;

排查并解決該問題;

二、分析過程

2.1 參數(shù)配置

JVM 參數(shù)配置如下:

-Xms3g -Xmx3g -Xmn1g -XX:MetaspaceSize=128m -XX:ParallelGCThreads=5 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError

新生代大?。?G;

新生代垃圾收集器:ParNewGC;

老年代大?。?G;

老年代垃圾收集器:ConcMarkSweepGC;

CMS觸發(fā)條件:老年代內(nèi)存占用達(dá)到80%及以上;

2.2 定位問題

1.由于報警的時間點(diǎn)都集中在上午的 8-12 點(diǎn)之間,懷疑是由于某個定時任務(wù)造成的;

2.定位具體的定時任務(wù),有兩個定時任務(wù)的時間設(shè)置基本滿足;

任務(wù)1: 更新客戶信息
CustomerScheduleJobService.updateCustomerDataDaily 0 0/30 8,9,10,11,12 * * ?
任務(wù)2: 創(chuàng)建客戶任務(wù)
CustomerStaffScheduleJobService.jobCreateTask 0 10,40 7,8,9,10,11 * * ?

3.確定具體的任務(wù)

確認(rèn)的兩個思路:

1.通過日志確認(rèn)定時任務(wù)的執(zhí)行時長等;

2.將2個定時任務(wù)分別指定不同的機(jī)器執(zhí)行觀察;

排查任務(wù)執(zhí)行時間:

任務(wù)1 : 很快,幾乎不處理業(yè)務(wù)邏輯;

[03-09 08:00:00 062 INFO ] [] [] [] [customerDataStat-pool-0] bll.customer.CustomerUpdateInfoDailyBll - (123) logid=6907112718471909376 [BizCustomerBll.updateCustomerDataDaily] thread begin...Ip: 10.151.49.157
[03-09 08:01:25 476 INFO ] [] [] [] [customerDataStat-pool-0] bll.customer.CustomerUpdateInfoDailyBll - (125) logid=6907112718471909376 [BizCustomerBll.updateCustomerDataDaily] end total=0Ip: 10.151.49.157

任務(wù)2: 執(zhí)行約35分鐘時間;

8:10分開始,8:45分結(jié)束;

[03-09 08:45:08 458 INFO ] [] [] [] [pool-4-thread-20] bll.task.CreateCustomerTaskBll - (109) logid=6907115234995589120 method=jobCreateTask msg=end queryRuleNum=7 queryCustomerNum=15962 createTaskCustomerNum=238 createTaskCount=271Ip: 10.151.49.157

基本確定為第二個定時任務(wù)導(dǎo)致FullGC;

2.3 JVM分析

2.3.1 單天監(jiān)控圖

內(nèi)存趨勢

GC趨勢

2.3.2 報警時間段監(jiān)控圖

內(nèi)存趨勢

GC趨勢

2.3.3 圖表分析

2.3.3.1 老年代變化

現(xiàn)象

1.任務(wù)執(zhí)行過程中:老年代有明顯增長,并且FullGC后并沒有特別明顯的下降,只有些許下降;

2.任務(wù)執(zhí)行結(jié)束后:下次任務(wù)開始執(zhí)行,進(jìn)行FullGC后,會降到跟其他機(jī)器一樣的水平,甚至內(nèi)存占用更低;

備注

新生代到老年代的幾種情況

1:大對象;

2:年齡足夠長,cms沒有設(shè)置,默認(rèn)是6,通過jinfo確認(rèn)也是6;

3:suvivor區(qū)不足以存放YGC后的存活對象,直接使用擔(dān)保策略晉升到老年代;

分析

任務(wù)執(zhí)行過程中,YGC平均1分鐘執(zhí)行5次,很多對象都會達(dá)到最大晉升年齡6,晉升到老年代;

并且由于任務(wù)沒有結(jié)束,對象還有引用,所以FullGC之后并沒有明顯下降;

上次任務(wù)結(jié)束后,老年代并沒有像suvivor區(qū)一樣有一段時間的低內(nèi)存占用,主要是直到下次任務(wù)開始后才會觸發(fā)新一次的FullGC,觸發(fā)后,老年代的對象由于任務(wù)結(jié)束后沒有引用了,所以會正?;厥?;

2.3.3.2 survivor區(qū)變化

suvivor區(qū)內(nèi)存總共100M,任務(wù)執(zhí)行過程中,平均占用 80M;高的時候會飆升到90以上,所以這個過程中YGC也變得很頻繁,平均1分鐘5次;

2.3.3.3 非堆內(nèi)存/方法區(qū)/compressed class cach變化

使用 jstat 分別統(tǒng)計了兩臺機(jī)器的gc統(tǒng)計,兩者最大的區(qū)別在于 執(zhí)行過定時任務(wù)的機(jī)器的MC(方法區(qū)大小) 以及 CCSC(壓縮類空間大小) 明顯比沒有執(zhí)行過定時任務(wù)的機(jī)器高很多;

任務(wù)執(zhí)行過程中方法區(qū)的內(nèi)存占用會跟老年代的曲線保持一致,這幾個區(qū)的回收也是靠老年代,這個通過grafana平臺的監(jiān)控圖也可以看出來;

2.3.3.4 dump文件分析

groovy相關(guān)的類占比57.57%;

2.4 參數(shù)配置

java 與 groovy 版本

java version "1.8.0_191"
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.15</version>
</dependency>

代碼中使用到groovy的地方:同樣是這個定時任務(wù),下發(fā)任務(wù)時,表達(dá)式檢驗(yàn)是否滿足下發(fā)條件,表達(dá)式是用groovy進(jìn)行處理的;

public class GroovyShellUtils {
private static LoggerHelper logger = LoggerHelper.getLoggerHelper(GroovyShellUtils.class);

public static boolean explain(String scriptText) {
try {
GroovyShell groovyShell = new GroovyShell();
Object evaluate = groovyShell.evaluate(scriptText);
return (boolean) evaluate;
} catch (Exception e) {
logger.error("", e);
}
return false;
}
}
// 使用:
for (String rule : rules) {
boolean res = GroovyShellUtils.explain(rule);
}

基本上可以定位問題在groovy腳本的加載處,groovy不合理使用會導(dǎo)致,動態(tài)生成很多新類,使得metaspace的不斷被占用;

class 對象在 1.8 及以后存放在 metaspace 中,也就是堆外內(nèi)存。

groovy每執(zhí)行一次,會將傳入的文本動態(tài)加載成一個腳本類,入?yún)⑹俏谋緯r,生成的文件名中包含了一個自增的數(shù)值,也就是每執(zhí)行一次都會動態(tài)生成一個新類,1個用戶7個任務(wù)規(guī)則校驗(yàn) * 15962個用戶 = 111734個

protected synchronized String generateScriptName() {
return "Script" + (++counter) + ".groovy";
}

GroovyShell 在內(nèi)部,它使用groovy.lang.GroovyClassLoader,這是在運(yùn)行時編譯和加載類的核心。

GroovyClassLoader 保留對其創(chuàng)建的所有類的引用,而 class 對象只有在被加載的 classloader 被回收的時候才會被回收,因此很容易造成內(nèi)存泄漏;

綜上分析,groovy 錯誤的使用方式導(dǎo)致 class 對象常駐堆外內(nèi)存且隨著調(diào)用頻率增長。

三、解決方案

1、每個腳本共用一個 GroovyShell 對象,不能使用 for 的方式,循環(huán)創(chuàng)建使用;

2、每次執(zhí)行完釋放對象 shell.getClassLoader().clearCache();

責(zé)任編輯:龐桂玉 來源: 轉(zhuǎn)轉(zhuǎn)技術(shù)
相關(guān)推薦

2021-05-13 08:51:20

GC問題排查

2023-04-06 07:53:56

Redis連接問題K8s

2021-11-23 21:21:07

線上排查服務(wù)

2021-03-29 12:35:04

Kubernetes環(huán)境TCP

2019-01-16 09:20:42

架構(gòu)設(shè)計JVM FullGC宕機(jī)事故

2019-06-24 08:17:55

CPUFullGCJava

2021-08-02 13:08:56

高并發(fā)服務(wù)

2019-03-26 08:52:51

2025-03-17 10:01:07

2022-01-10 10:26:30

Kubernetes抓包環(huán)境

2018-01-19 11:12:11

HTTP問題排查

2021-12-02 07:50:30

NFS故障內(nèi)存

2022-07-13 08:31:18

React問題排查

2019-03-15 16:20:45

MySQL死鎖排查命令

2022-02-08 17:17:27

內(nèi)存泄漏排查

2023-01-04 18:32:31

線上服務(wù)代碼

2019-04-15 13:15:12

數(shù)據(jù)庫MySQL死鎖

2022-08-01 20:29:48

分布式架構(gòu)數(shù)據(jù)

2017-12-19 14:00:16

數(shù)據(jù)庫MySQL死鎖排查

2020-11-02 09:48:35

C++泄漏代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號