记一次GC调优

问题描述

最近在做一个项目,遇到了一些GC方面的问题,所以记录一下。

  • 第一个问题:在大量数据的时候,需要new大量对象;
  • 第二个问题:JVM参数的设置;

在我项目中就会遇到很多new大量对象的问题,以及JVM参数设置的问题。

下面我们来详细说说我的问题,我这边的具体问题是给用户推送相关文章,借助用户特征,给一些文章候选集算法排序,从而给每个用户推断出相对有兴趣的文章。平台有百万级用户,用户会有很多属性,文章候选集会有2000多篇;由于在排序的时候,会将每个用户的特征和文章的特征组织好给传给排序接口进行排序操作,所以在这个过程中就会遇到new大量对象;在排序的过程中也会new出大量的需要排序的向量data。

JVM参数介绍

-Xms :初始堆大小
-Xmx :最大堆大小 
-XX:NewSize=n :设置年轻代大小 
-XX:NewRatio=n: 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 
-XX:SurvivorRatio=n :年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5 
-XX:MaxPermSize=n :设置持久代大小 收集器设置 
-XX:+UseSerialGC :设置串行收集器 
-XX:+UseParallelGC :设置并行收集器 
-XX:+UseParalledlOldGC :设置并行年老代收集器 
-XX:+UseConcMarkSweepGC :设置并发收集器 垃圾回收统计信息 
-XX:+PrintHeapAtGC GC的heap详情 -XX:+PrintGCDetails GC详情 
-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径
-XX:+PrintTenuringDistribution 打印年龄信息等 
-XX:+HandlePromotionFailure 老年代分配担保(true or false) 并行收集器设置 
-XX:ParallelGCThreads=n :设置并行收集器收集时使用的CPU数。并行收集线程数。 
-XX:MaxGCPauseMillis=n :设置并行收集最大暂停时间 
-XX:GCTimeRatio=n :设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) 并发收集器设置 
-XX:+CMSIncrementalMode :设置为增量模式。适用于单CPU情况。 
-XX:ParallelGCThreads=n :设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数
-XX: PretenureSizeThreshold 大对象直接进入老年区

JVM堆 详细介绍

  • 新生代
    • Eden Space
    • From Survivor Space
    • To Survivor Space
  • 老年代

详细介绍

问题一:在大量数据的时候,需要new大量对象

由于new大量对象导致问题,Eden Space区迅速占满,然后会频繁年轻代GC,从而导致JVM大量的精力和时间都花在了GC上,于是就会影响程序运行效率;而我的程序对效率运行要求比较高,为300多W用户排序推断出10篇用户最感兴趣的文章,生成文件;需要在50分钟内完成从Mysql数据库中取出用户、候选集文章、然后通过排序将300多W用户数据写入文件。

解决办法:借助Object Pool的概念,不要new大量数据;比如一开始,我会去组装一个rank model对象,把用户特征、文章特征组装成一个对象,然后一共有2000多篇文章,每个用户每次需要new出2000多个文章对象,然后组装成一个用户的rank model对象。这里面300W*2000数据量大家可以算一下,会new出多少对象,这里我只是举出一个地方的例子;其实一开始实现功能是没有问题,但是就是效率慢,后面通过Object Pool的概念,发现我这里可以利用一些对象,比如文章候选集这个可以重复利用,直接传到rank对象中进行排序,这样我们就只要new2000多次;

后面还有就是在rank对象里面,一开始每个用户去排序的时候,都会new出一个需要排序的data对象来;一开始我们会把这些对象放入到一个List中;这样每个用户排序的时候都会组建一个List,然后刚开始的时候我们也没有对List进行初始化大小;这个list的大小其实是可以确定,就是我们需要排序的特征向量,文章特征+用户特征组装。第一次修改,将List固定好大小,这样就不用经常给List扩容,但是修改这个之后,还是没能很大的解决效率问题;
第二次,我将List改成了数组,然后所有用户共用一个数组,每次每个用户都向数组里面填充数据,这样,就会大大减小new对象,以及充分利用对象池的概念。最后这样基本解决了我的问题,效率提升到了1个小时左右。

问题二: JVM参数设置

上面也介绍我的第一个问题,由于第一个问题中会new大量对象,所以一开始我的JVM堆区的Eden Space很快就会占满;GC log如下:

[GC (Allocation Failure) [PSYoungGen: 2774574K->32144K(3424256K)] 3915335K->1172906K(5895680K), 0.0262079 secs] [Times: user=0.16 sys=0.01, real=0.03 secs]

要让log中打出GC日志,我们需要在Java程序运行的时候加上参数:-XX:+PrintGCDetails,这个在上面也具体介绍了JVM参数设置。

上面的log会频繁的打出,并且Eden Space 区基本在98%-100%的占用量之间。从而导致了频繁GC,并且Eden Space一直处于占满的状态,也就是new对象比GC释放还快,大大降低了效率。于是我通过修改-XX:NewSize=2000m来加大Eden Space区域,这样会导致我们的老年代区域变小。后面在修改完代码后,大大降低了new对象,但是我在之前调整了Eden Space,忘记修改回来,导致我的程序Eden Space空间比较大,老年代空间比较小,这时候在程序运行一段时间后,JVM log中会频繁出现Full GC,log 如下:

[Full GC (Ergonomics) [PSYoungGen: 18971648K->4339495K(20749824K)] [ParOldGen: 8683369K->8683028K(8683520K)] 27655017K->13022524K(29433344K), [Metaspace: 13816K->13816K(1062912K)], 31.9796242 secs] [Times: user=563.28 sys=0.66, real=31.98 secs]

这时候我突然想起来,我之前设置过Eden Space区域,调大Eden Space区域。由于老年代区域比较小,所以在程序运行一段事件后,导致老年代占满了,从而频繁的Full GC,这时候我们应该知道Full GC时间比较慢,一般在10s左右,所以我们应该让JVM尽量在年轻代就释放,而尽量减少Full GC;所以我调整了JVM参数,降低Eden Space区域,增大了年老代区域,于是确实跟我所想的一样,大大降低了Full GC的次数,也大大增加了我程序的运行效率。

经过最后的努力,程序效率确实提升到了50分钟左右。

书籍推荐

《深入理解Java虚拟机_JVM高级特性与最佳实践》

原博客地址

TommyYang's Blog

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容

  • 原文阅读 前言 这段时间懈怠了,罪过! 最近看到有同事也开始用上了微信公众号写博客了,挺好的~给他们点赞,这博客我...
    码农戏码阅读 5,881评论 2 31
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,020评论 11 349
  • 1.一些概念 1.1.数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始...
    落落落落大大方方阅读 4,456评论 4 86
  • 废话不多说,自己进入今天的主题 1、面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: - 抽象:...
    传奇内服号阅读 2,289评论 1 31
  • 你说我看不起人,一下子就把我的火烧起来了。 对,我就是看不起你! 我看不起因为你用了孩子的餐具,再给她拆一套或者他...
    原罪_198e阅读 404评论 0 1