问题首次出现:
1,找运维查看cpu 内存 网络,结果运维说还没有监控 欲哭无泪。
没办法既然没有监控,立马查看jvm 垃圾回收日志,并且在系统统计每隔几个小时就查看
垃圾回收的日志,发现年轻代,老年代一切都非常正常,就这样过了两天突然又挂了,再次
查看垃圾回收文件发现年轻代数据都很正常,突然发现full gc 居然很频繁很不正常因为我查看了
平常的数据发现老年代的使用率非常低, 既然这么低为什么会导致full gc不合理。
2,突然想起来每次挂都是挂在某个方法的执行,之前没有引起注意 是公司架构组写的一个JSONUtils.toJson的方法
完全没怀疑这个问题, 带着疑问百度了下, 然后查到了 (https://blog.csdn.net/xyghehehehe/article/details/78820135)
原来是metaspace 导致的内存溢出, 然后立马找到框架组写的代码
JSONSerializer serializer =new JSONSerializer(out, new SerializeConfig());
我操问题找到每次new SerializeConfig() 对象导致每次序列化都会创建动态对象具体见
(https://blog.csdn.net/xyghehehehe/article/details/78820135)
知识点:
这个问题解决虽然简单,但是导致问题发生的原因还是值得重视。为什么在升级之前不会出现这个问题?这就要分析jdk1.8和1.7自带的hotspot虚拟机的差异了。从jdk1.8开始,自带的hostspot虚拟机取消了过去的永久区,而新增了metaspace区,从功能上看,metaspace可以认为和永久区类似,其最主要的功用也是存放类元数据,但实际的机制则有较大的不同。首先,metaspace默认的最大值是整个机器的物理内存大小,所以metaspace不断扩张会导致java程序侵占系统可用内存,最终系统没有可用的内存;而永久区则有固定的默认大小,不会扩张到整个机器的可用内存。当分配的内存耗尽时,两者均会触发full gc,但不同的是永久区在full gc时,以堆内存回收时类似的机制去回收永久区中的类元数据(Class对象),只要是根引用无法到达的对象就可以回收掉,而metaspace判断类元数据是否可以回收,是根据加载这些类元数据的Classloader是否可以回收来判断的,只要Classloader不能回收,通过其加载的类元数据就不会被回收。这也就解释了我们这两个服务为什么在升级到1.8之后才出现问题,因为在之前的jdk版本中,虽然每次调用fastjson都创建了很多代理类,在永久区中加载类很多代理类的Class实例,但这些Class实例都是在方法调用是创建的,调用完成之后就不可达了,因此永久区内存满了触发full gc时,都会被回收掉,而使用1.8时,因为这些代理类都是通过主线程的Classloader加载的,这个Classloader在程序运行的过程中永远也不会被回收,因此通过其加载的这些代理类也永远不会被回收,这就导致metaspace不断扩张,最终耗尽机器的内存了。
————————————————