打印[Loaded sun.reflect.GeneratedMethodAccessor…]原因的定位

这篇博客主要探讨了在Java应用中出现[Loaded sun.reflect.GeneratedMethodAccessor...]日志的原因,这实际上是JDK在运行时生成的反射调用优化类。博主分析了Spring AOP在生成代理对象时的流程,指出这可能是由于使用了AOP导致的,特别是在Spring 4.2.4之前的版本中,ObjenesisStd非静态常量可能导致 PermGen 空间持续增长,引发Full GC。解决方案是升级Spring到4.2.4及以上版本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    在查看线上日志的时候发现时不时有以下日志打印出来:

    [Loaded sun.reflect.GeneratedMethodAccessor167 from __JVM_DefineClass__]

截图如下:

    开始感觉很奇怪为什么时不时会打印这些东西,一开始以为是jdk配置的问题,就去度娘查了一下。然后看到了这篇文章:

http://rednaxelafx.iteye.com/blog/548536

    简单来说这是Sun的JDK中关于方法反射调用的一个实现细节,为了提高多次反射调用时的效率而在运行时生成专用的Java类实现调用逻辑。

    因为该日志由反射产生,所以初步猜测这应该是项目中使用了AOP生成代理类时产生的log。下面梳理一下AOP代理的具体流程。

    Spring有两种生成代理对象的方法,第一种是通过ProxyFactory,第二种是通过ProxyFactoryBean。第一种获取比较简单,但是需要手工的进行写代码,而第二种是通过Spring的IOC机制来控制Bean的生成。但是无论是ProxyFactory或者ProxyFactoryBean都是要通过createAopProxy().getProxy()来获取相应的代理对象。以下通过跟踪ProxyFactoryBean的代码来查找原因:

    首先,找到ProxyFactoryBean的getObject方法

    Spring Bean 默认scope="singleton" 即Bean是单例的,只有当在Bean中指明 scope="prototype" 时 Bean 才是多例形式。在 getObject() 方法中判断是要代理的类是单例还是多例的,然后调用对应的方法。

    单例模式时调用getSingletonInstance()方法,在getSingletonInstance方法中引入了super中的方法,super是指ProxyCreatorSupport,这里ProxyCreatorSupport是ProxyFactoryBean和ProxyFactory的父类,已经做了很多工作,只需在ProxyFactoryBean的getObject()方法中通过父类的createAopProxy()取得相应的AopProxy。

    跟踪createAopProxy方法,追踪到了ProxyCreatorSupport中,然后,借助了AopProxyFactory,此时得到的aopProxyFactory,在构造函数中已经定义为new DefaultAopProxyFactory() 进入DefaultAopProxyFactory中,找到createAopProxy方法,在这里判断是调用JDK动态或者CGlib动态中的一种。

 

    

    在这里有个bug,在spring4.2.4之前的版本中ObjenesisStd 并没有被声明为static类型,如图2 所以在使用Cglib方式生成代理类时,每次调用createAopProxy时会执行以下代码:

    return new ObjenesisCglibAopProxy(config);

    此时会实例化一个新的 private final ObjenesisStd objenesis; 实例。

图1 spring-4.0.5.RELEASE

图2 spring-4.2.4.RELEASE

那么这个问题会有什么影响呢?

当通过ObjenesisCglibAopProxy去生成代理类的时候,会调用createProxyClassAndInstance()方法。

在这个方法内部会调用 this.objenesis.newInstance(enhancer.createClass()); newInstance方法继承自父类ObjenesisBase,该方法最终调用的是getInstantiatorOf()方法,这个方法里面有个cache,主要是保证一个代理增强类对象只有一个class字节码。

 

 

 

    所以,当ObjenesisStd 为非静态常量时,每次new ObjenesisCglibAopProxy(config); 时cache都会失效,所以会去调用:

org.springframework.objenesis.strategy.SingleInstantiatorStrategy.newInstantiatorOf(Class)方法。

以下是后面的方法调用链:

1、java.lang.reflect.Constructor.newInstance(Object...)

2、java.lang.reflect.Constructor.acquireConstructorAccessor()

3、sun.reflect.ReflectionFactory.newConstructorAccessor(Constructor)

4、sun.reflect.MethodAccessorGenerator.generateConstructor(Class, Class[], Class[], int)

5、sun.reflect.MethodAccessorGenerator.generate(Class, String, Class[], Class, Class[], int, boolean, boolean, Class) 6、sun.reflect.MethodAccessorGenerator.generateName(boolean, boolean)

当执行到generateName()方法时,我们就能看到文章开始时打印出的那些的字符串了。

    当需要被代理的要声明为非单例时 scope="prototype",由于代理类调用非常频繁时,会不断产生增强类字节码,会引发PermGen持续增长。当PermGen满了时就会引发FullGC。此时建议Spring升级到4.2.4以后版本。

转载于:https://my.oschina.net/blackgladiolus/blog/854395

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值