记一次JVM内存溢出解决过程

2 篇文章 0 订阅

1.问题简述

线上的服务器随着访问量的增加莫名其妙的挂了查看了一下错误日志:

java.lang.OutOfMemoryError: Compressed class space

2.问题分析

在Java8以前,有一个选项是UseCompressedOops。所谓OOPS是指“ordinary object pointers“,就是原始指针。Java Runtime可以用这个指针直接访问指针对应的内存,做相应的操作(比如发起GC时做copy and sweep)。
那么Compressed是啥意思?64bit的JVM出现后,OOPS的尺寸也变成了64bit,比之前的大了一倍。这会引入性能损耗——占的内存double了,并且同尺寸的CPU Cache要少存一倍的OOPS。
于是就有了UseCompressedOops这个选项。打开后,OOPS变成了32bit。但32bit的base是8,所以能引用的空间是32GB——这远大于目前经常给jvm进程内存分配的空间。
到了Java8,永久代被干掉了,有了“meta space”的概念,存储jvm中的元数据,包括byte code,class等信息。Java8在UseCompressedOops之外,额外增加了一个新选项叫做UseCompressedClassPointer。这个选项打开后,class信息中的指针也用32bit的Compressed版本。而这些指针指向的空间被称作“Compressed Class Space”。默认大小是1G,但可以通过“CompressedClassSpaceSize”调整。

如果你的java程序引用了太多的包,有可能会造成这个空间不够用,于是会看到

java.lang.OutOfMemoryError: Compressed class space

既然是空间不够用了那么是不是调大CompreseedClassSpaceSize就可以了.

3.解决问题

3.1设置参数

JVM设置了两个选项可以调节CompreseedClassSpaceSize大小:

-XX:+UseCompressedClassPointers(压缩开关)
-XX:CompressedClassSpaceSize(Compressed Class Space 空间大小限制)。

jvm配置参数设置为:

JAVA_OPTS="-server -Dfile.encoding=UTF-8 -Xms2g -Xmx4g -Xmn1g -Xss512K -verbose:gc -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=10 -XX:PermSize=1g -XX:MaxPermSize=1g -XX:CompressedClassSpaceSize=3g "

-XX:+UseCompressedClassPointers 是需要 -XX:+UseCompressedOops 开启的,所以堆大小要是大于 32G,CompressedOops 自动关闭,CompressedClassPointers 也会关闭的,关闭了就没有 Compressed Class Space 了,这块就是 Class Space 了。-XX:CompressedClassSpaceSize 大小的设置也是有限制,因为压缩开关是受制于 32G,所以这个自然也是不能大于 32G,不过 hotspot 规定了这个参数不准大于 3G,所以这个参数其实是不能大于 3G。

3.2 验证

使用

ps aux|grep java

查询出jvm进程的PID
在这里插入图片描述
使用

jcmd PID GC.heap_info

查看内存使用情况,可以看出来内存是不断快速增长的
在这里插入图片描述
为了更方便的查找问题,配置使用Java VisualVM 工具查看具体信息具体安装方式查看
安装VisualVM .
在这里插入图片描述
可以看出内存还是在不断的增长过程中,当达到设置的最大内存大小时候就会出现内存溢出。

3.3 分析

使用Java VisualVM工具生成堆dump文件,然后将文件下载到本地,使用mat软件分析内存泄露原因。
或者可以在 tomcat 内使用命令生成 dump 文件

jmap -dump:live,format=b,file=/home/heap.hprof PID

使用mat打开生成的dump文件,在overview视图下打开Leak Suspects选项,生成内存泄露分析。
在这里插入图片描述
可以看出内存泄露是由于 org.codehaus.groovy.reflection.ClassInfo 引起的.

打开idea的maven查找这个类发现是由ShardingJDBC使用的
在这里插入图片描述
使用JVM参数可以打印出执行过程中类的加载卸载过程

-XX:+TraceClassLoading 跟踪类的加载
-XX:+TraceClassUnloading 跟踪类的卸载

每次请求执行都会打印

Script1 from file:/groovy/shell

在这里插入图片描述
这里的分表表达式使用groovy进行动态编译,生成Java class info, 而其位于 方法区; 不停的生成对象, 触发 full gc, 而full gc并不能回收多少对象; 导致的现象就是, 内存不断增长。

3.4 解决

新版的Sharding jdbc使用了缓存, 而不是每次请求都使用 groovy动态编译,
有FGC问题的读写分离版本是 3.0.0.M1,升级成为3.1.0版本即可。
在这里插入图片描述
升级后可以看到内存不在持续增长。

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值