最近上线一个定时任务:根据条件查询数据,然后生成excel,当成邮件中的附件发送给指定用户。在这个定时任务执行的时候,从gc的日志部分如下:
2016-10-12T15:10:56.017+0800: 2130.042: [GC2016-10-12T15:10:56.017+0800: 2130.042: [ParNew: 605431K->65838K(613440K), 0.0397940 secs] 1115602K->592670K(2029056K), 0.0400670 secs]
[Times: user=0.19 sys=0.01, real=0.04 secs]
2016-10-12T15:10:56.173+0800: 2130.198: [GC2016-10-12T15:10:56.173+0800: 2130.198: [ParNew: 611182K->68096K(613440K), 0.0386650 secs] 1138014K->610941K(2029056K), 0.0390110 secs]
[Times: user=0.20 sys=0.01, real=0.04 secs]
2016-10-12T15:10:56.313+0800: 2130.338: [GC2016-10-12T15:10:56.313+0800: 2130.338: [ParNew: 613440K->68096K(613440K), 0.0391930 secs] 1156285K->627219K(2029056K), 0.0394650 secs]
[Times: user=0.21 sys=0.01, real=0.04 secs]
2016-10-12T15:10:56.475+0800: 2130.500: [GC2016-10-12T15:10:56.475+0800: 2130.500: [ParNew: 613440K->65122K(613440K), 0.0415260 secs] 1172563K->640188K(2029056K), 0.0418330 secs]
[Times: user=0.24 sys=0.01, real=0.04 secs]
2016-10-12T15:10:56.655+0800: 2130.680: [GC2016-10-12T15:10:56.655+0800: 2130.680: [ParNew: 610466K->68096K(613440K), 0.0363720 secs] 1185532K->659268K(2029056K), 0.0366540 secs]
[Times: user=0.21 sys=0.01, real=0.04 secs]
从这部分日志中就可以年轻代gc使用的垃圾收集器为ParNew,并且收集频率相当高,一秒就执行了很多次,显然是很不合理的。从中可以想到有以下几种情况:
1、大量新对象生成
2、年轻代空间较小
然后我们又观察了一下老年代的情况,发现老年代被占用空间迅速增长,而且很快就把老年代空间占满了。这时候gc日志也有错误出现了,如下:
2016-10-12T15:14:34.289+0800: 2348.314: [Full GC2016-10-12T15:14:34.289+0800: 2348.314: [CMS2016-10-12T15:14:35.172+0800: 2349.197: [CMS-concurrent-mark: 1.151/1.181 secs] [Times:
user=3.36 sys=0.05, real=1.18 secs]
(concurrent mode failure): 1415615K->1415468K(1415616K), 5.3406530 secs] 2029055K->1492615K(2029056K), [CMS Perm : 159147K->159147K(262144K)], 5.3409430 secs] [Times: user=6.19 s
ys=0.01, real=5.34 secs]
发生了concurrent mode failure,此错误就是在CMS GC的过程中,又有对象需要放到old区,但是old区空间不足了,放不下了就报了此错误。
从以上情况能够看出,年轻代的对象很快的进入了old区,而且数量还比较大,在old区进行gc的时候,又有很多年轻代要进入old区,但是old区空间已经满足不了要求了。
这时候我们查看了系统运行时,jvm的参数,如下:
-XX:CMSFullGCsBeforeCompaction=0 -XX:CMSInitiatingOccupancyFraction=80 -XX:+DisableExplicitGC -XX:ErrorFile=/var/sankuai/logs/waimai-bizdata-task.vmerr.log.2016
10121435 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/sankuai/logs/waimai-bizdata-task.heaperr.log.201610121435 -XX:InitialHeapSize=2147483648 -XX:MaxHeapSize=2147483648
-XX:MaxNewSize=697933824 -XX:MaxPermSize=268435456 -XX:MaxTenuringThreshold=6 -XX:NewSize=697933824 -XX:OldPLABSize=16 -XX:OldSize=1395867648 -XX:PermSize=134217728 -XX:+PrintComm
andLineFlags -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCMSCompactAtFullCollection -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+
UseParNewGC
可以看到-XX:MaxTenuringThreshold=6(默认值是15),即对象经历多少次年轻代回收之后仍存活,就会放入老年代中,且-XX:NewSize=697933824年轻代总共有600多M。
再看一下堆内存-XX:MaxHeapSize=2147483648,总共是2G的空间,明显比较小,使用CMS收集器的话,最好有大的内存和强力的CPU,内存最好大于3G。
通过CMSInitiatingOccupancyFraction<=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior+2)))/(Xmx-Xmn)*100这个公式的计算,大致得出CMSInitiatingOccupancyFraction<=53。
通过以上分析,发现以下问题:
1、堆内存偏小,应该设置大一些,如6G
2、-XX:MaxTenuringThreshold设置也应该大一些(这里有必要说一下,虽然显示这个参数值被修改了,但这个参数并没有被设置,而是设置的其他参数默认修改了这个参数,所以使用CMS的一些参数时,要特别注意,最好打印一下被修改的参数,这样能够清楚具体什么参数被修改了,-XX:NewSize也是一样的)
3、XX:CMSFullGCsBeforeCompaction此值最好设置为1,XX:CMSInitiatingOccupancyFraction此值设置偏大了一点
根据以上分析,对一些参数做了修改之后,重新运行程序,只是减缓了出错的时间点,但是依然发生同样的问题。
这时我们利用jmap -dump:live,format=b,file=out [pid]命令打印了一下程序的堆内存,然后使用mat进行分析。这里有个插曲,由于dump下来的文件比较大,mat加载时一直报错java heap space,从中能够看出mat内存不够。mat在界面上无法修改使用的内存,需要在他的配置文件MemoryAnalyzer.ini中修改-Xmx1024m为你需要的内存大小。
这时候等mat把dump文件加载之后,发现以下对象比较多
发现这个对象是生成excel的,后来上网查了一下,发现是因为使用了XSSFWorkbook,这个对象会把所有数据都放在内存中,占用着内存,所以内存使用量很大。在POI3.8之后,提供了SXSSFWorkbook,这个是支持流处理,通过限制内存中可访问的记录行数来实现低内存利用,当达到限定值时,新一行数据的加入会引起老一行的数据刷新到硬盘,比如内存中限制行数为100,当行号达到101时,行号为0的记录会被刷新到硬盘,并从内存删除,以此类推。
SXSSFWorkbook对象生成时,可以指定内存中限制的行数,以rowAccessWindowSize属性来标识,默认的就是100。
使用SXSSFWorkbook这个生成excel之后,在运行程序就正常了,gc也不是很频繁了,比较平稳。
相关推荐
然而,CMS在内存压力过大时,可能会出现“Concurrent Mode Failure”(并发模式失败)问题,此时GC需要暂停应用进行“预清理”,以避免在并发标记阶段因空间不足而被迫暂停应用。为了避免这种情况,可以通过调整`-XX...
- **避免promotion failed和concurrent mode failure**: 可以通过调整SurvivorSpace大小或增大Old区来降低这两种情况的发生几率。 - **GCCycleInitiation调优**: 控制Live Data Size和分配速率,避免过早或过晚...
"promotion failed"表示Survivor区无法容纳更多晋升的老年代对象,"concurrent mode failure"是CMS并发模式失败,意味着GC过程中出现了意外。 理解这些概念对于优化Java应用程序的性能至关重要,因为不恰当的内存...
2. 垃圾收集策略:包括 Minor GC(年轻代收集)、Full GC(全局收集)和 Concurrent Mode Failure(并发模式失败)。调优时需关注垃圾收集频率、暂停时间及内存利用率。 3. 内存分配与回收:通过调整新生代与老年代...
此外,如果在并发标记阶段对象的创建速率过高,可能导致`concurrent mode failure`,这时需要进行Full GC。这些问题可以通过调整JVM参数或选择其他垃圾收集器来解决。 总的来说,CMS垃圾收集器是Java性能调优中一个...
虽然CMS能减少停顿时间,但其清除过程可能导致内存碎片,且在低内存和高并发情况下可能出现"Concurrent Mode Failure"问题。 在选择垃圾收集器时,应考虑应用的特性、系统资源以及性能需求。例如,对于桌面应用或轻...
- **空间预留问题**:CMS需要在回收过程中预留足够的内存供应用使用,否则可能导致Concurrent Mode Failure错误。 - **浮动垃圾**:由于在并发清除阶段可能产生新的垃圾,这些垃圾需要在下一次GC中处理。 - **碎片...
- 避免promotion failed和concurrent mode failure:调整SurvivorSpace大小或老年代大小,降低触发比率,确保GC过程稳定高效。 综上所述,《Sun JDK 1.6内存管理—调优篇》提供了丰富的JVM内存管理和GC调优知识,...
4. **并发模式失败(Concurrent Mode Failure)**:当并发收集开始后,如果剩余堆空间不足以支持应用程序运行,就会触发并发模式失败,此时JVM会暂停所有应用线程以完成垃圾回收。 5. **增量模式(Incremental Mode...
当出现"Concurrent Mode Failure"时,JVM会切换到Serial Old收集器进行老年代的回收,以确保系统的稳定。 4. **并行化目标与吞吐量** 并行化GC的主要目的是提高吞吐量,即程序运行时间与总时间的比例。Parallel ...
是指需要大量连续内存空间的 java 对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发 JVM 进行 Full GC。...
- CMS垃圾收集器运行时出现promotion failed或concurrent mode failure。 - 统计发现Minor GC后的对象大小超过老年代剩余空间。 **1.3 堆的分代** JVM的堆被分为年轻代(Young Generation)、年老代(Tenured ...