本文链接:https://blog.csdn.net/a15939557197/article/details/90635460
背景
前段时间有一个这样的需求:第三方调用接口,30分钟内调用120W次;

物理机(与线上配置一样)上压测,第一次压了20w次,没有出现问题;接着又压了20w次,出现了内存溢出问题。

java.lang.OutOfMemoryError: Metaspace
JVM配置 
JAVA_OPT_MEM="
-server -Xms4096M -Xmx4096M -Xmn512M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M
-XX:+UseParNewGC //新生区使用CMS(标记清理)算法进行垃圾回收
-XX:+UseConcMarkSweepGC //新生代使用并行收集器,老年代使用 CMS+串行收集器
...
...
"
 初始化最大元空间是512M,

增加两个参数,在Full GC前后分别对内存做一个heap dump

-XX:+HeapDumpBeforeFullGC
-XX:+HeapDumpAfterFullGC
观察GC日志

{Heap before GC invocations=2799 (full 1):
par new generation total 471872K, used 58664K [0x00000006c0000000, 0x00000006e0000000, 0x00000006e0000000)
eden space 419456K, 9% used [0x00000006c0000000, 0x00000006c2679e10, 0x00000006d99a0000)
from space 52416K, 36% used [0x00000006dccd0000, 0x00000006ddfa0218, 0x00000006e0000000)
to space 52416K, 0% used [0x00000006d99a0000, 0x00000006d99a0000, 0x00000006dccd0000)
concurrent mark-sweep generation total 3670016K, used 131775K [0x00000006e0000000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 258955K, capacity 262086K, committed 262144K, reserved 1265664K
class space used 45934K, capacity 46565K, committed 46592K, reserved 1048576K
2019-04-02T17:22:31.606+0800: 1359.334: [Full GC (Metadata GC Threshold) 2019-04-02T17:22:31.606+0800: 1359.334: [Heap Dump (before full gc): , 2.0610454 secs]2019-04-02T17:22:33.667+0800: 1361.395: [CMS2019-04-02T17:22:33.812+0800: 1361.540: [CMS-concurrent-mark: 0.163/2.264 secs] [Times: user=2.46 sys=0.25, real=2.26 secs]
(concurrent mode failure): 131775K->104031K(3670016K), 0.7241683 secs]2019-04-02T17:22:34.391+0800: 1362.119: [Heap Dump (after full gc): , 1.9020867 secs] 190439K->104031K(4141888K), [Metaspace: 258955K->258955K(1265664K)], 4.6899705 secs] [Times: user=4.35 sys=0.49, real=4.69 secs]
Heap after GC invocations=2800 (full 2):
par new generation total 471872K, used 0K [0x00000006c0000000, 0x00000006e0000000, 0x00000006e0000000)
eden space 419456K, 0% used [0x00000006c0000000, 0x00000006c0000000, 0x00000006d99a0000)
from space 52416K, 0% used [0x00000006dccd0000, 0x00000006dccd0000, 0x00000006e0000000)
to space 52416K, 0% used [0x00000006d99a0000, 0x00000006d99a0000, 0x00000006dccd0000)
concurrent mark-sweep generation total 3670016K, used 104031K [0x00000006e0000000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 258416K, capacity 260922K, committed 262144K, reserved 1265664K
class space used 45826K, capacity 46371K, committed 46592K, reserved 1048576K
}
问题
1、FullGC的时候 Metaspace 怎么现在变成了1.2G,设置的不是512M嘛?

[Metaspace: 258955K->258955K(1265664K)]
查看相关资料和官方文档后,发现Metaspace还有一个区间是Klass Metaspace,由参数-XX:CompressedClassSpaceSize进行控制,参考你假笨,笨神的博客 JVM源码分析之Metaspace解密 ;JDK8的时候 Klass Metaspace默认是1G。

2、Metaspace 空间在GC前后为什么根本没有被垃圾回收?

[Metaspace: 258955K->258955K(1265664K)]
从上面的GC日志分析,我们看到了Full GC前后,Metaspace的使用变化是从258955K->258955K,说明GC根本没有进行回收垃圾;

Metaspace主要是类的一些元数据信息,主要源于类加载器,可以使用 jstat -class pid 查看类的加载和写在情况;

还可以使用 jmap -histo:live pid  查看ClassLoader对象比较多的类型;

Metaspace中类的元数据信息只有在加载它的ClassLoader被释放后才会发生写在,如果ClassLoader对象一直存活,那么它所加载的类的元数据信息将不会被卸载。

参考笨神的博客 JVM源码分析之JDK8下的僵尸(无法回收)类加载器 ,摘取重要的信息如下:

类加载器创建过多,带来的一个问题是,在类加载器第一次加载类的时候,会在Metaspace里会给它分配内存块,为了分配高效,每个类加载器用来存放类信息的内存块都是独立的,所以哪怕你这个类加载器只加载一个类,也会为之分配一块空的内存给这个类加载器,其实是至少两个内存块,于是你有可能会发现Metaspace的内存使用率非常低,但是committed的内存已经达到了阈值,从而触发了Full GC,如果这种只加载很少类的类加载器非常多,那造成的后果就是很多碎片化的内存。

处理办法
修改代码使用反射完成对象之间属性的copy,改为set方法进行实现,解决了该问题;

总结
如果项目中有大流量的调用接口,一定要警惕大流量带来的大量反射类加载器创建引起的GC不回收问题。
————————————————
版权声明:本文为CSDN博主「沿途风景21」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a15939557197/article/details/90635460

JDK8记FullGC时候Metaspace内存不会被垃圾回收的更多相关文章

  1. JDK8 的FullGC 之 metaspace

    JDK8 的FullGC 之 metaspace - 简书https://www.jianshu.com/p/1a0b4bf8d498

  2. JVM内存管理和JVM垃圾回收机制

    JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采 ...

  3. JVM内存管理机制和垃圾回收机制

    JVM内存管理机制和垃圾回收机制 JVM结构 图片描述: java源码编译成class文件 class文件通过类加载器加载到内存 其中方法区存放的是运行时的常量.静态变量.类信息等,被所有线程共享 堆 ...

  4. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...

  5. JVM堆内存控制/分代垃圾回收

    JVM的堆的内存, 是通过下面面两个参数控制的 -Xms 最小堆的大小, 也就是当你的虚拟机启动后, 就会分配这么大的堆内存给你 -Xmx 是最大堆的大小 当最小堆占满后,会尝试进行GC,如果GC之后 ...

  6. JVM内存管理中的垃圾回收策略

    JVM垃圾回收策略 1.静态内存分配和回收 编译时已经确定了内存空间大小,程序被加载后则一次性分配好内存空间.程序结束后,则对应栈帧撤销,分配的静态内存空间则被回收. 2.动态内存分配和回收 程序运行 ...

  7. 内存缓存机制and垃圾回收机制

    一.内存缓存机制 var_dump(memory_get_usage(true)); $a="laruence"; var_dump(memory_get_usage(true)) ...

  8. 内存分析_.Net垃圾回收介绍

    垃圾回收 1.       .Net垃圾回收中涉及的名称 1.1.什么是代? 垃圾回收器为了提升性能使用了代的机制,共分为三代(Gen0.Gen1.Gen2).GC工作机制基于以下假设, 1)  对象 ...

  9. JVM·垃圾收集器与内存分配策略之垃圾回收算法!

    1.垃圾回收算法    1.1.标记-清除算法(Mark-Sweep):             过程分为“标记”和“清除”两个过程.先将所有需要回收的目标统一标记,然后再统一清除.          ...

随机推荐

  1. Servlet和JSP学习总结

    目录 Jsp会被编译成servlet,在页面被第一次访问的时候 Jsp中可以在html页面中嵌入java代码或者引入jsp标签 可以在html中引入自定义标签 Web工程的目录结构 Jsp的注释 Js ...

  2. Google C++单元测试框架GoogleTest---AdvancedGuide(译文)

    上篇在这里: 下篇在这里.

  3. Azkaban无法启动错误Error: Could not find or load main class 12321

    1 错误日志 Using Hadoop from /mnt/software/hadoop-2.6.0-cdh5.16.1 bin/internal/../.. /mnt/software/jdk1. ...

  4. 如何入门Pytorch之二:如何搭建实用神经网络

    上一节中,我们介绍了Pytorch的基本知识,如数据格式,梯度,损失等内容. 在本节中,我们将介绍如何使用Pytorch来搭建一个经典的分类神经网络. 搭建一个神经网络并训练,大致有这么四个部分: 1 ...

  5. STM32——CAN总线波特率和位时序详解

    本人用的单片机是STM32F407,其它型号的单片机类似,可做参考! 一.标准CAN协议位时序概念 由于CAN属于异步通讯,没有时钟信号线,连接在同一个总线网络中的各个节点会像串口异步通讯那样,节点间 ...

  6. HTML5新特性——1 HTML5音频

    注意:<source>同一个音乐可以添加不同格式的音频,这样就可以满足不同浏览器都能够正常播放,兼容不同浏览器. 代码示例: <!doctype html> <html ...

  7. Quartz框架介绍

    一.Quartz概述: Quartz是一个完全由Java编写的开源任务调度的框架,通过触发器设置作业定时运行规则,控制作业的运行时间.其中quartz集群通过故障切换和负载平衡的功能,能给调度器带来高 ...

  8. SQL 查询今天、昨天、7天内、30天的数据

    今天的所有数据: 昨天的所有数据: 7天内的所有数据: 30天内的所有数据: 本月的所有数据: 本年的所有数据: 查询今天是今年的第几天: select datepart(dayofyear,getD ...

  9. ubuntu NGINX uwsgi https 部署Django 遇到的问题

    搞了3天终于把Django成功部署到Ubuntu,记录一下: 引用来自泡泡茶壶: Ubuntu下的Nginx + Uwsgi + Django项目部署详细流程 前提说明: Django作为小程序的后端 ...

  10. 解决Spring AOP Controller 不生效

    在spring-mvc.xml文件中,进行以下配置,就可以实现在Controller中, 方法一:最简单的,在spring-mvc.xml配置文件中,添加以下语句 spring-mvc.xml < ...