背景
最近在搞一些大数据智能推荐方面的开发工作,为了保证推荐的实时性,没隔10分钟会启动几个worker遍历所有数据 进行检查。
程序在预发环境运行一段时间后,偶尔会出现堆内存使用率超过80%的情况(公司监控系统默认堆内存使用率超过80%后,就会报警)。
重启后 一段时间内存使用情况是正常的,所以初步怀疑有存在内存泄露。
分析问题
1、按照内存泄露流程排查:ps -ef|grep java 找到对应java工程的进程号(也可以用top)
进程号为6206
2、然后执行jmap -dump:live,file=/tmp/dump.dat 6206,dump文件有点大(几百M),下载下来用 eclipse Memory Analyzer 进行分析(参考
http://blog.csdn.net/xb151652000/article/details/8056792)
遗憾的是没有发现异常,难道不是内存泄漏?
3、查看gc情况:jstat -gcutil 6206
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 8.33 11.44 76.30 98.24 95.28 9493 1170.869 2 1.964 1172.833
0.00 8.33 11.47 76.30 98.24 95.28 9493 1170.869 2 1.964 1172.833
0.00 8.33 11.47 76.30 98.24 95.28 9493 1170.869 2 1.964 1172.833
0.00 8.33 11.47 76.30 98.24 95.28 9493 1170.869 2 1.964 1172.833
0.00 8.33 11.48 76.30 98.24 95.28 9493 1170.869 2 1.964 1172.833
发现 年老代内存使用率占比,超过70%。初步发现原因所在:YGC 年轻代gc太频繁了(9000多次,从程序启动时算起),FGC fullgc 正常总共就2次。
4、再使用命令:jmap -heap 6206看下各个分代内存大小:
我晕,s0 s1都自动分配的1M。
先看下YGC流程:我们知道每次YGC时,存活下来的对象先从eden复制到s0,年龄为1,第二次YGC还存活从s0复制到s1 年龄为2,第三次YGC 从s1复制到s0 年龄变为3,当到达最大年龄是再复制到年老代(最大年龄配置-XX:MaxTenuringThreshold=4)。
这里s0 s1太小了,根本放不下每次eden中存活下来的对象,形同虚设,所以只能直接迁移到年老代,导致年老代很快打满(YGC时,年轻代内存使用率已经100%)。出现堆内存报警,就不足为奇了。之后就会频繁的出现fullgc(年老代被打满),进入恶性循环。
每隔10分钟YGC次数(worker执行时):
解决问题
问题原因:由于我们没有指定堆内存大小,系统默认分配,并根据内存动态调整各个分代内存大小,最终导致s0 s1区分配过小,每次YGC都将存活的对象迁移到年老代。
解决办法,jvm 参数手动调整为:
-Xmx2048m -Xms2048m -Xmn1g -Xss512k -XX:SurvivorRatio=8 -XX:+UseParallelGC
-XX:MaxTenuringThreshold=4 -XX:ParallelGCThreads=43
-Xmx2048m 最大堆内存2G(之前默认是1G)
-Xms2048m 初始堆内存(建议跟-Xmx设置成一样,避免gc后调整)
-Xmn1g 年轻代内存1G
-Xss512k 每个栈大小为512K(设置小点,可以创建更多的线程,设置太小 会栈溢出)
-XX:SurvivorRatio=8 算法(s0+s1)/eden=2:8,即s0=s1=1/10 * 1G=102.4M
-XX:+UseParallelGC 开启并行gc(我们是后端服务,需要大吞吐量)
-XX:MaxTenuringThreshold=4 年轻代对象年龄为4
-XX:ParallelGCThreads gc线程数,此值最好配置与处理器数目相等。
对比
重新启动程序,看下内存分配情况(jmap -heap pid):
eden = 820M
s0=102M
s1=102M
old=1024M
gc效果(jstat - gcutil pid):
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
28.51 0.00 22.69 5.31 98.04 95.71 6 1.687 2 1.353 3.040
28.51 0.00 22.69 5.31 98.04 95.71 6 1.687 2 1.353 3.040
28.51 0.00 22.69 5.31 98.04 95.71 6 1.687 2 1.353 3.040
28.51 0.00 22.84 5.31 98.04 95.71 6 1.687 2 1.353 3.040
28.51 0.00 22.84 5.31 98.04 95.71 6 1.687 2 1.353 3.040
通过长时间观察O(年老代) 内存使用率长时间维持在 6%左右,对象在s0 s1区里来回复杂,基本都已经被gc掉了。至此问题解决。
优化后,每隔10分钟 YGC次数:
小结
jvm 参数优化不是千篇一律,需要根据具体的业务场景具体调整(我这里是每隔10分钟有一个峰值)。深刻理解每个参数具体含义和用法,反复实践才是关键。
开发中也尽量注意代码质量,比如大数据开发中,遍历10万条数据,一定要分配次 一次少量取数据处理,取下一批数的时候让上一批数据成为垃圾。
尽量不要增量的使用全局的MAP List,如果一定要使用,记得清理。
线程池中使用ThreadLocal,也要注意在结束时remove。
相关推荐
Java的发展历程始于1995年,伴随着众多技术的引入和版本的更新,包括AWT、JDBC、JavaBeans、JDK1.1、J2SE、泛型、NIO、JMX等,每一次技术的更新都对JVM和Java程序的执行效率和功能产生了重大影响。OpenJDK的出现使得...
- **复制算法**(Copying):将内存分为大小相等的两块,每次只使用其中一块,在垃圾回收时,将还活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这种算法适用于新生代,因为每次回收都有大量...
2. jmap -histo:live <pid>:此命令用于查看当前存活的实例,执行过程中可能会触发一次Full GC。 当需要对JVM进行内存溢出处理时,可以通过两个参数来实现自动导出堆内存信息。这两个参数为: 1. -XX:+...
JVM调优是一个复杂而细致的过程,涉及对内存管理、垃圾回收等多个方面的综合考量。理解不同的垃圾回收算法及其适用场景是提高Java应用程序性能的关键。通过合理选择和配置垃圾回收器,可以显著提升系统的响应速度和...
- **青年代(Young Generation)**:大部分对象在这里创建并快速死亡,经历一次或几次Minor GC后,存活对象晋升至老年代。 - **老年代(Old Generation)**:长期存活的对象,经历了多次Minor GC后,它们被移动...
在深入讨论JVM(Java虚拟机)调优之前,我们有必要先了解一下虚拟机的基本概念和堆栈...通过上述的分析和总结,我们可以得出,JVM调优是一个涉及多方面知识的复杂过程,需要开发者具备扎实的理论基础和丰富的实践经验。
5. 经过第一次Minor GC后仍然存活的对象会被移动到Survivor区,并且每次GC后年龄+1。 6. 当对象年龄达到一定阈值时,会被移至老年代。 #### 五、年轻代中的GC 在年轻代中,JVM需要确保To Survivor空间为空。在每次...
JVM调优是一个持续的过程,需要根据应用的具体情况进行调整。上述参数只是起点,还需要通过监控工具(如JVisualVM或JConsole)分析GC日志,观察不同GC策略对应用程序的影响,以及内存使用、吞吐量、停顿时间等指标。...
JVM调优是一项重要的技术实践,旨在优化Java应用的性能,减少内存消耗,提高响应速度,确保系统的稳定运行。本示例“JVM调优示例2”将聚焦于如何通过调整JVM参数来优化Java应用。 首先,我们来理解JVM调优的关键...
- **示例**:`-XX:MaxTenuringThreshold=0` 表示所有经过第一次Minor GC后存活的对象直接晋升到老年代。 #### 9. -XX:+UseParallelGC 和 -XX:ParallelGCThreads - **定义**: - `-XX:+UseParallelGC` 启用并行...
JVM 调优是 Java 虚拟机(Java Virtual Machine)中的一种技术,用于优化 Java 应用程序的性能和内存使用。其中,垃圾回收(Garbage Collection)是 JVM 调优的重要组成部分,本文将详细介绍 JVM 调优中的垃圾回收...
它负责运行Java应用程序,是Java“一次编写,到处运行”理念的核心。JVM调优是提升Java应用性能的关键步骤,而深入理解Hotspot源码则有助于我们更高效地进行优化工作。以下将详细探讨这些知识点。 1. JVM整体结构...
在对Java虚拟机(JVM)进行调优的过程中,我们首先...总体而言,JVM调优是一个系统化的过程,它需要对JVM的工作机制有深入的理解,并且结合实际应用进行测试和调优。有效的JVM调优能够显著提升Java应用的性能和稳定性。
选择合适的垃圾回收器和调整其参数是JVM调优的重要工作,比如可以通过-XX:+UseSerialGC、-XX:+UseParallelGC等选项来指定使用哪种收集器,同时需要根据应用程序的特性调整新生代、老年代和持久代的大小,以及垃圾...
### jvm调优建议文档知识点解析 #### 一、JVM基本概念及组成部分 - **JVM内存区域划分**:JVM内存分为新生代、老年代以及元空间(Metaspace)三大区域。其中,新生代负责存放新创建的对象,经过多次垃圾回收后存活的...
大多数对象在Eden区被创建,经过第一次垃圾回收后,仍然存活的对象会被移动到Survivor区之一。 2. 老年代:经历过多次垃圾回收仍存活的对象会晋升到老年代。这个区域的内存回收相对复杂,因为对象生命周期较长,且...
- **分阶段调优**:逐步进行调优,避免一次性尝试太多变更。 #### 实践 - **内存调优**: - **堆大小调整**:根据应用的实际需求设置合适的堆大小。 - **垃圾收集器选择**:不同类型的垃圾收集器适用于不同的...
在现代互联网应用中,JVM调优是一项至关重要的任务,特别是在处理高并发、大数据量的业务场景中。以lendengine为例,其在架构设计中,为了提高日志处理效率,采用了中间件优化,如降低日志推送频率和更换压缩算法,...