游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

慧销平台ThreadPoolExecutor内存泄漏分析

  作者:京东零售冯晓涛问题背景
  京东生旅平台慧销系统,作为平台系统对接了多条业务线,主要进行各个业务线广告,召回等活动相关内容与能力管理。
  最近根据告警发现内存持续升高,每隔23天会收到内存超过阈值告警,猜测可能存在内存泄漏的情况,然后进行排查。根据24小时时间段内存监控可以发现,容器的内存在持续上升:
  问题排查
  初步估计内存泄漏,查看24小时时间段jvm内存监控,排查jvm内存回收情况:
  YoungGC和FullGC情况:
  通过jvm内存分析和YoungGC与FullGC执行情况,可以判断可能原因如下:
  1、存在YoungGC但是没有出现FullGC,可能是对象进入老年代但是没有到达FullGC阈值,所以没有触发FullGC,对象一直存在老年代无法回收
  2、存在内存泄漏,虽然执行了YoungGC,但是这部分内存无法被回收
  通过线程数监控,观察当前线程情况,发现当前线程数7427个,并且还在不断上升,基本判断存在内存泄漏,并且和线程池的不当使用有关:
  通过JStack,获取线程堆栈文件并进行分析,排查为什么会有这么多线程:
  发现通过线程池创建的线程数达7000:
  代码分析
  分析代码中ThreadPoolExecutor的使用场景,发现在一个worker公共类中定义了一个线程池,worker执行时会使用线程池进行异步执行。publicclassBackgroundWorker{privatestaticThreadPoolExecutorthreadPoolExecutor;static{init(15);}publicstaticvoidinit(){init(15);}publicstaticvoidinit(intpoolSize){threadPoolExecutornewThreadPoolExecutor(3,poolSize,1000,TimeUnit。MINUTES,newLinkedBlockingDeque(1000),newThreadPoolExecutor。CallerRunsPolicy());}publicstaticvoidshutdown(){if(threadPoolExecutor!null!threadPoolExecutor。isShutdown()){threadPoolExecutor。shutdownNow();}}publicstaticvoidsubmit(finalRunnabletask){if(tasknull){return;}threadPoolExecutor。execute((){try{task。run();}catch(Exceptione){e。printStackTrace();}});}}
  广告缓存刷新worker使用线程池的代码:publicclassAdActivitySyncJob{Scheduled(cron005?)publicvoidexecute(){log。info(AdActivitySyncJobstart);ListDicDTOlocationListlocationService。selectLocation();if(CollectionUtils。isEmpty(locationList)){return;}中间省略部分无关代码BackgroundWorker。init(40);locationCodes。forEach(locationCode{showChannelMap。forEach((key,value){BackgroundWorker。submit(newRunnable(){Overridepublicvoidrun(){log。info(AdActivitySyncJob,locationCode:{},showChannel:{},locationCode,value);ResultresultnotLoginAdActivityOuterService。getAdActivityByLocationInner(locationCode,ImmutableMap。of(showChannel,value));LocalCache。ADACTIVITYCACHE。put(locationCode。concat()。concat(value),result);}});});});log。info(AdActivitySyncJobend);}PostConstructpublicvoidinit(){execute();}}
  原因分析:猜测是worker每次执行,都会执行init方法,创建新的线程池,但是局部创建的线程池并没有被关闭,导致内存中的线程池越来越多,ThreadPoolExecutor在使用完成后,如果不手动关闭,无法被GC回收。分析验证
  验证局部线程池ThreadPoolExecutor创建后,如果不手动关闭,是否会被GC回收:publicclassTest{privatestaticThreadPoolExecutorthreadPoolExecutor;publicstaticvoidmain(String〔〕args){for(inti1;i100;i){每次均初始化线程池threadPoolExecutornewThreadPoolExecutor(3,15,1000,TimeUnit。MINUTES,newLinkedBlockingDeque(1000),newThreadPoolExecutor。CallerRunsPolicy());使用线程池执行任务for(intj0;j10;j){submit(newRunnable(){Overridepublicvoidrun(){}});}}获取当前所有线程ThreadGroupgroupThread。currentThread()。getThreadGroup();ThreadGrouptopGroupgroup;遍历线程组树,获取根线程组while(group!null){topGroupgroup;groupgroup。getParent();}intslackSizetopGroup。activeCount()2;Thread〔〕slackThreadsnewThread〔slackSize〕;获取根线程组下的所有线程,返回的actualSize便是最终的线程数intactualSizetopGroup。enumerate(slackThreads);Thread〔〕atualThreadsnewThread〔actualSize〕;System。arraycopy(slackThreads,0,atualThreads,0,actualSize);System。out。println(ThreadssizeisatualThreads。length);for(Threadthread:atualThreads){System。out。println(Threadname:thread。getName());}}publicstaticvoidsubmit(finalRunnabletask){if(tasknull){return;}threadPoolExecutor。execute((){try{task。run();}catch(Exceptione){e。printStackTrace();}});}}
  输出:
  Threadssizeis302
  Threadname:ReferenceHandler
  Threadname:Finalizer
  Threadname:SignalDispatcher
  Threadname:main
  Threadname:MonitorCtrlBreak
  Threadname:pool1thread1
  Threadname:pool1thread2
  Threadname:pool1thread3
  Threadname:pool2thread1
  Threadname:pool2thread2
  Threadname:pool2thread3
  Threadname:pool3thread1
  Threadname:pool3thread2
  Threadname:pool3thread3
  Threadname:pool4thread1
  Threadname:pool4thread2
  Threadname:pool4thread3
  Threadname:pool5thread1
  Threadname:pool5thread2
  Threadname:pool5thread3
  Threadname:pool6thread1
  Threadname:pool6thread2
  Threadname:pool6thread3
  执行结果分析,线程数量302个,局部线程池创建的核心线程没有被回收。
  修改初始化线程池部分:初始化一次线程池threadPoolExecutornewThreadPoolExecutor(3,15,1000,TimeUnit。MINUTES,newLinkedBlockingDeque(1000),newThreadPoolExecutor。CallerRunsPolicy());for(inti1;i100;i){使用线程池执行任务for(intj0;j10;j){submit(newRunnable(){Overridepublicvoidrun(){}});}}
  输出:
  Threadssizeis8
  Threadname:ReferenceHandler
  Threadname:Finalizer
  Threadname:SignalDispatcher
  Threadname:main
  Threadname:MonitorCtrlBreak
  Threadname:pool1thread1
  Threadname:pool1thread2
  Threadname:pool1thread3解决方案
  1、只初始化一次,每次执行worker复用线程池
  2、每次执行完成后,关闭线程池
  BackgroundWorker的定位是后台执行worker均进行线程池的复用,所以采用方案1,每次在static静态代码块中初始化,使用时无需重新初始化。
  解决后监控:
  jvm内存监控,内存不再持续上升:
  线程池恢复正常且平稳:
  Jstack文件,观察线程池数量恢复正常:
  Dump文件分析线程池对象数量:
  拓展1、如何关闭线程池
  线程池提供了两个关闭方法,shutdownNow和shutdown方法。
  shutdownNow方法的解释是:线程池拒接收新提交的任务,同时立马关闭线程池,线程池里的任务不再执行。
  shutdown方法的解释是:线程池拒接收新提交的任务,同时等待线程池里的任务执行完毕后关闭线程池。2、为什么threadPoolExecutor不会被GC回收threadPoolExecutornewThreadPoolExecutor(3,15,1000,TimeUnit。MINUTES,newLinkedBlockingDeque(1000),newThreadPoolExecutor。CallerRunsPolicy());
  局部使用后未手动关闭的线程池对象,会被GC回收吗?获取线上jump文件进行分析:
  发现线程池对象没有被回收,为什么不会被回收?查看ThreadPoolExecutor。execute()方法:
  如果当前线程数小于核心线程数,就会进入addWorker方法创建线程:
  分析runWorker方法,如果存在任务则执行,否则调用getTask()获取任务:
  发现workQueue。take()会一直阻塞,等待队列中的任务,因为Thread线程一直没有结束,存在引用关系:ThreadPoolExecutorWorkerThread,因为存在GCROOT的引用,所以无法被回收。

关于读书我在岛屿读书关于读书读书的目的在于觉醒和改变。多读书不一定让你变的更优秀,但多读书可以让你坦然的接受自己的平凡。曾有人让我推荐几本书,我说喜欢读什么就读什么吧……大雪过后,不差钱的话把这3宝端上桌,营养解馋,手脚都暖和老话说秋冬进补,开春打虎,进入寒冬之后,人们的活动量减少,经常会出现怕冷的情况,除了穿的暖和之外,更要吃的好。饮食上不能太过油腻和辛辣,容易造成脂肪堆积,所以在冬天,既要补充热……你是不是讨好型人格记得几年前参加活动,主办方安排的是双人间,同住的另一位是只加了微信还没怎么聊过天的陌生女孩。我先到酒店,发现双人间里的两张床视野悬殊,靠外的那张,旁边是书桌、沙发、落地窗,窗外……用心和不断的修正自己的不足,接纳孩子的成长2023年2月14日8宝家书分享:关于孩子的教育我的参与极少,陪伴也是屈指可数,缺失对孩子的教育,总以工作忙为借口,而忽视了陪伴和孩子的教育。有了妹妹后全家的重心全在妹妹……新研究发现,重力仍在从深处塑造地球表面像所有行星一样,地球是重力的产物。渐渐地,越来越多的尘埃和岩石吸收了足够多的物质,形成了一个不断扩大的矿物球体,我们现在称之为家。直到今天,重力继续以比我们想象的更微妙的……4款低价高配手机,均搭载骁龙870处理器,最低仅1699元您在阅读前请点击上面的关注二字,后续会第一时间为您提供更多有价值的相关内容,感谢您的支持。骁龙870处理器虽然是去年的芯片,但是该芯片放到今年,手机的性能也足够使用,关键……麦肯锡2022年人工智能现状及五年回顾干货报告原文:ThestateofAIin2022andahalfdecadeinreviewTHENEXTGENERATIONOFTRUCKS来源:麦肯锡,2022年12月发布……央行下调首套个人住房公积金贷款利率购房成本与负担进一步降低经济日报北京讯(记者姚进)9月30日,中国人民银行宣布,决定自2022年10月1日起,下调首套个人住房公积金贷款利率0。15个百分点,5年以下(含5年)和5年以上利率分别调整为……迪丽热巴性感高清壁纸,细腻的皮肤,精致的脸蛋,仙女的气质轻轻柔柔的光幕落下,很快从光幕中走出一名白衣年轻女子。女子玉颜雅致,肌肤胜雪,身姿曼妙,目若秋水,唇不点而红,柔美中带着种雍容华贵。又是一个倾城绝色的大美人。叶洪涛抱着胳……失眠后,先别急着吃安眠药,先看看你找对病因了吗?提到睡眠问题,大家各有各的烦恼。要么是入睡困难,要么好不容易睡着了,但是一直不停的做梦,早上起床后身体非常疲惫,感觉睡了一个假觉。要么就是容易醒,很多粉丝都给我反馈一个问……弄巧成拙的悲喜草或许心里有事,总放不下兴奋的惦记,一向起床很迟的我,这天却醒得很早,簇新的阳光斜照进来映得雪白的粉墙一派欢快的晕红院里的枣树上晨鸟叽叽喳喳跳叫着,油然增添些许喜悦。我穿戴整齐,……最近火了一种上短下长的穿法,叫短上衣阔腿裤,适合小个子要说最近什么穿法最火,那上短下长的搭配方式一定榜上有名!精致时髦的短款上衣搭配上慵懒随意的宽松感阔腿裤,直接通过单品的组合就能在视觉上最大程度的发挥修饰身材的效果,显瘦又显高,……
为啥中国科技人才比不上外国?四个故事或许能解释美国拜登政府主导的芯片法案后,并且联合了韩、日和欧洲等国家,对我国科技产业进行了掐脖子式的打击。因此在本次两会中,国家高层把科技创新提到一个重中之重的位置!从两会中,我们……Zoom宣布与OpenAI合作,4月向受邀客户推出AI助手智东西编译吴菲凝编辑李水青智东西3月28日消息,昨日,美国视频会议公司Zoom宣布与AI研发公司OpenAI启动合作,将于4月开始向受邀客户推出新的AI助手工……360数科更名奇富科技B站诉优酷侵权bilibili晚会索赔一、今日头条上海一辆蔚来试驾车冲进人行道,致一死一伤二、财经新动向飞鱼科技:推出保卫萝卜4等新游戏,预期2022年亏损净额同比减少67。4爱博医疗:20……肾衰竭的这4个症状最容易被误诊,小心因误诊变成尿毒症肾病是一种隐身能力很强的疾病,很多患者从肾病到尿毒症可能没有任何不适,一旦出现明显症状时,往往已经进展到了中后期。这期间肾脏可能毫无察觉,反而在其它器官和系统产生症状,从……台积电警告,低端芯片出现短缺由于诸多因素的影响,今年下半年芯片的价格开始出现暴跌。更有报道说芯片从2021年的200元价格暴跌到了20元。出现了芯片产能过剩,企业大量砍单的现象。大家都在潜意识里面有……北京冬奥会中国体育代表团领奖服发布北京冬奥会中国体育代表团领奖服今天(12月31日)发布。领奖服以传统文化意境和汉字中为设计灵感,加入了小立领和一字扣等中式元素,清晰的红色线条中轴线寓意世界各地的伙伴相聚中国,……紧急裁员6000人!全球最大中资鞋厂也撑不住了!传越南宝元鞋厂将裁员6000人本周二,据外媒取得的一份胡志明市劳动局文件显示,宝成集团旗下的越南宝元鞋厂将裁退近3000名工厂员工,稍晚一些时间,还有3000名员工将不再……文旦和柚子区别简单,记住这4点,再也认不错外观不同:文旦上面小,下面大,整体比柚子更长,果皮发绿,带淡黄色,柚子外形多是圆形的,表皮偏黄色,且比较厚。口感不同:文旦果粒小,但没有酸味,甘甜可口;柚子果粒大,吃起来……龙佰集团布局海绵钛大棋实现三连冠金秋十月,果实累累。2022年对于中国钛工业的发展注定是不平凡的一年,傲娇的业绩,傲娇的成果必将载入史册飘香的果实为祖国母亲的生日、为喜迎党的二十大召开献上了一份厚礼!……CBA三消息山东进行两大补强,马布里赛后生气,萨林杰不是水货爱国篮,爱CBA,我是洛姐,小伙伴们看完记得点赞!山东队这个赛季的成绩排在联盟里的中流,第一阶段球队在没有外援的情况下已经不能算是四强球队了,特别是他们的主力球员和替补球……河南旅游绿博花讯夏花烂漫正当时一季一变换,一月一风景,还未来得及反应,夏至临近,灸热的气息也愈发浓郁。每年六月,最先出现在您脑海里的是什么?是夏天的阳光、夏天的风雨,还是绚烂的夏花?没错!伴着夏……经常散步对肺部有什么好处?提醒肺可能喜欢这6件事,可以多做肺是人体的呼吸器官,肺在我们身体当中是起到交换气体的作用,所以肺是最主要的一个换气器官。肺主呼吸之气,是说肺行使呼吸功能,在肺内进行气体交换,吸入自然界的清气,呼出体内的……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网