从一起GC血案谈到反射原理

本文详细分析了一起由于大量反射导致的Java Full GC问题,探讨了Perm区(在JDK8中为Metaspace)的内存占用,反射原理,包括Method调用、动态生成的GeneratedMethodAccessor类,以及并发情况下可能产生的性能开销。文章指出,解决此类问题的方法包括升级JDK以允许G1在GC过程中卸载类,更换序列化协议避免反射,或调整软引用回收策略。

前言

首先回答一下提问者的问题。这主要是由于存在大量反射而产生的临时类加载器和 ASM 临时生成的类,这些类会被保留在 Metaspace,一旦 Metaspace 即将满的时候,就会触发 FullGc,已达到回收不再被使用的类对象的目的。具体问题请参考接下来的内容,更好的了解反射的实现原理。

正文

概述

公司之前有个大内存系统(70G以上)一直使用CMS GC,不过因为该系统对时间很敏感,偶尔会因为gclocker导致remark特别长(虽然加了-XX:+CMSScavReengeBeforeRemark参数,但是gclocker会导致remark前的YGC被delay),无法忍受这么长的暂停就只好迁移到了G1,经过一系列的调优之后算比较稳定了,这套参数便推到了全部机器上

可是就在上周突然有机器出现了Full GC,本来G1设计出来就是希望Full GC不在出现,出现Full GC一般是不正常,GC日志如下:
gc.jpg

从上面日志不难发现是因为Perm触发的Full GC,并且Full GC之后Perm就降下去了,不过需要提一下的是JDK7下正常的G1 GC是不会做类卸载的,只有Full GC的时候才会卸载,但JDK8下是提供了相关参数的可以在G1 GC某些阶段做类卸载

于是要业务方先做了coredump,保存好现场再重启系统,然后再针对coredump做了heap dump,不过heapdump有40G这么大,可以通过jmap -permstat core.xxx来看看究竟perm里有什么东西

这篇文章相对来说比较长,涉及到的知识点比较多,如果实在忍不住看下去,可以跳到最后看下我对这个问题的描述再反过来看这篇文章或许让你有更清晰的认识

Perm里究竟塞了什么

既然是Perm满了,那我们得看Perm里究竟放了什么,我们知道Perm里主要存的是类的原始数据,比如我们加载了一个类,那这个类的信息会在Perm里分配内存来存储它的一些数据结构,所以大部分情况下,Perm的使用量和加载的类个数是关系很大的,当然Perm里在低版本的时候还会存一些其他的数据,比如String(String.intern()的情况)。

另外经验告诉我们如果真的是Perm溢出,那有地方动态构建一个类加载器加载一个类的可能性会很大,通过上面的jmap命令,我们可以统计下sun.reflect.DelegatingClassLoader的个数居然达到了415737个

那基本可以锁定是反射类加载器导致Perm溢出的原因了,那究竟为什么会有这么多反射类加载器呢,反射类加载器又是什么,接下来先简单说下反射的原理

反射的原理

反射大家用起来很方便,由于性能其实也比较不错了,因此用得挺广的,我们通常这么用反射

Method method = XXX.class.getDeclaredMethod(xx,xx);method.invoke(target,params)

不过这里我不准备用大量的代码来描述其原理,而是讲几个关键的东西,然后将他们串起来

获取Method

要调用首先要获取Method,而获取Method的逻辑是通过Class这个类来的,而关键的几个方法和属性如下:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeapDump性能社区

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值