cglib使用不慎引发的Java内存泄漏

【深入理解Java虚拟机】笔记1:借助CGLIB使元空间内存溢出 一、示例代码 public class JavaMethodAreaOOM { static class OOMObject{ } public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMOb... 阅读详情

本次只为演示在使用中出现的Java内存泄漏的问题,以及如何解决这样的问题,cglib版本为cglib-nodep-2.2.jar。
cglib的应用是非常多的,但是当我们使用它的时候,如果一不小心,等出了问题再去查,就比较杯具了。所以最好的解决方案就是写代码时就注意这些细节。(当然了,不能指望在开发阶段不引入Bug)
近期项目在做压力测试,暴露了内存泄漏的Bug,cglib的使用不当便是原因之一。
下面来介绍代码。
清单1:
package com.jn.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;

/** *//**
 * 步骤方法拦截器.<br>
 *
*/
publicclass CglibLeak1 {

public<T> T newProxyInstance(Class<T> clazz) {
return newProxyInstance(clazz, new MyInterceptor(), new MyFilter());
 }

/** *//**
 * 创建一个类动态代理.
 * 
* @param <T>
 * @param superclass
 * @param methodCb
 * @param callbackFilter
 * @return
*/
publicstatic<T> T newProxyInstance(Class<T> superclass, Callback methodCb, CallbackFilter callbackFilter) {
 Enhancer enhancer =new Enhancer();
 enhancer.setSuperclass(superclass);
 enhancer.setCallbacks(new Callback[] { methodCb, NoOp.INSTANCE });
 enhancer.setCallbackFilter(callbackFilter);

return (T) enhancer.create();
 }

/** *//**
 * 实现MethodInterceptor接口
 * 
* @author l
 *
*/
class MyInterceptor implements MethodInterceptor {
 @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
returnnull;
 }
 }

/** *//**
 * 实现CallbackFilter接口
 * 
* @author l
*/
class MyFilter implements CallbackFilter {
 @Override
publicint accept(Method method) {
// Do some thing
return1;
 }
 }

/** *//**
 * 测试代码
 * @param args
 * @throws InterruptedException
*/
publicstaticvoid main(String args[]) throws InterruptedException {
 CglibLeak1 leak =new CglibLeak1();
int count =0;
while(true) {
 leak.newProxyInstance(Object.class); // 为了测试缩写
 Thread.sleep(100);
 System.out.println(count++);
 }
 }
}


用JProfiler来观察内存对象情况。
运行了一段时间(几十秒钟吧),内存对象的情况如图所示:

我们看到 MyFilter 的Instance count 已经达到了1266个,而且随着程序的继续运行,Instance count还在不断飙升,此情此景让人心寒。
而且在JProfiler上点击 Run GC 按钮 这些对象并不会被回收。内存泄漏啦。

原因就是cglib自身的内部代理类缓存,将MyFilter对象加入到了缓存中,以至于该对象很大、并发量很大时,会造成内存溢出的Bug。

既然知道了原因,解决办法就很明显了。
1.重写MyFilter类的equals和hashCode方法,这样,当MyFilter对象准备进入缓存时,cglib会判断是否为不同的MyFilter对象,如果是才加入到缓存。
我们重写了equals和hashCode后,让cglib认为这些MyFilter对象都是相同的。
2.将MyFilter类设置为静态类。原理都是相同的。

我以第二种解决方案来修改代码,请看。
清单2:

 

package com.jn.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;

/** *//**
 * 步骤方法拦截器.<br>
*/
publicclass CglibLeak {

privatestatic MethodInterceptor myInterceptor =new MethodInterceptor() {
 @Override
public Object intercept(Object obj,
 Method method, Object[] args,
 MethodProxy proxy) throws Throwable {
// do some things
returnnull;
 }
 };
// 创建实例
privatestatic CallbackFilter myFilter =new MyFilter();

publicstatic<T> T newProxyInstance(Class<T> clazz) {
return newProxyInstance(clazz, myInterceptor, myFilter);
 }

/** *//**
 * 实现CallbackFilter接口
 *
 * @author l
*/
staticclass MyFilter implements CallbackFilter {
 @Override
publicint accept(Method method) {
// Do some thing
return1;
 }
 }

/** *//**
 * 创建一个类动态代理.
 *
 * @param <T>
 * @param superclass
 * @param methodCb
 * @param callbackFilter
 * @return
*/
publicstatic<T> T newProxyInstance(Class<T> superclass, Callback methodCb,
 CallbackFilter callbackFilter) {
 Enhancer enhancer =new Enhancer();
 enhancer.setSuperclass(superclass);
 enhancer.setCallbacks(new Callback[]{methodCb, NoOp.INSTANCE});
 enhancer.setCallbackFilter(callbackFilter);

return (T)enhancer.create();
 }

/** *//**
 * 测试代码
 *
 * @param args
 * @throws InterruptedException
*/
publicstaticvoid main(String args[]) throws InterruptedException {
int count =0;
while (true) {
 newProxyInstance(Object.class); // 为了测试缩写
 Thread.sleep(100);
 System.out.println(count++);
 }
 }
}

 


运行后的结果应该很明显了:


MyFilter的Instance count 一直为1.
问题解决了。

因为我的MyFilter类中没有成员变量,所以在多线程并发访问时也不会出现问题。

如果以方案1 来解决这个内存泄漏问题情况是怎样的呢?
清单3:

 

package com.jn.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;

/** *//**
 * 步骤方法拦截器.<br>
 */
public class CglibLeak {

    private static MethodInterceptor myInterceptor = new MethodInterceptor() {
                                                       @Override
                                                       public Object intercept(Object obj,
                                                           Method method, Object[] args,
                                                           MethodProxy proxy) throws Throwable {
                                                           // do some things
                                                           return null;
                                                       }
                                                   };

    public <T> T newProxyInstance(Class<T> clazz) {
        return newProxyInstance(clazz, myInterceptor, new MyFilter());
    }

    /** *//**
     * 实现CallbackFilter接口
     *
     * @author l
     */
    class MyFilter implements CallbackFilter {
        @Override
        public int accept(Method method) {
            // Do some thing
            return 1;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof MyFilter) {
                return true;
            }
            return false;
        }

        @Override
        public int hashCode() {
            return 10011; // 没什么原则,只为测试
        }
    }

    /** *//**
     * 创建一个类动态代理.
     *
     * @param <T>
     * @param superclass
     * @param methodCb
     * @param callbackFilter
     * @return
     */
    public static <T> T newProxyInstance(Class<T> superclass, Callback methodCb,
        CallbackFilter callbackFilter) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(superclass);
        enhancer.setCallbacks(new Callback[]{methodCb, NoOp.INSTANCE});
        enhancer.setCallbackFilter(callbackFilter);

        return (T)enhancer.create();
    }

    /** *//**
     * 测试代码
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String args[]) throws InterruptedException {
        CglibLeak l = new CglibLeak();
        int count = 0;
        while (true) {
            l.newProxyInstance(Object.class); // 为了测试缩写
            Thread.sleep(100);
            System.out.println(count++);
        }
    }
}


运行一段时间后(几十秒),JProfiler的观测结果为:

MyFilter的对象还是很多,这是不是就表明 内存泄漏的问题依然存在呢。
当然不是,因为JVM垃圾回收策略的原因,我们new出来的MyFilter对象并不是 一旦成为垃圾就立即 被回收的。
经观察,当Instance count 为600左右时,GC会将这些“垃圾”回收。

解决问题之际感慨一下:Java内存问题无处不在啊
Java Metaspace OOM排查与优化:从原理到实战解决内存泄漏 在Java虚拟机(JVM)内存管理中,元空间(Metaspace)是存储类元数据的关键区域,取代了传统的永久代(PermGen)。其核心原理在于从本地内存动态分配空间,用于存放类定义、方法字节码、常量池等信息。当应用频繁进行动态类加载时,如大量使用反射、CGLib动态代理或框架字节码增强,元空间可能因类元数据累积而耗尽,引发OutOfMemoryError。这一问题的技术价值在于,它直接影响JVM的稳定性和性能,尤其在微服务架构和长期运行的应用中尤为突出。典型的应用场景包括Spring AOP代理、热部署环 阅读详情

相关推荐

JVM之内存泄漏和内存溢出

严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。内存溢出(out of memory):简单地说内存溢出就是指程序运行过程中申请的内存大于系统能够提供的内存,导致无法申请到足够的内存,于是就发生了内存溢出。:内存泄漏指程序运行过程中分配内存给临时变量,用完之后却没有被GC回收,始终占用着内存,既不能被使用也不能分配给其他程序,于是就发生了内存泄漏。:简单地说内存溢出就是指程序运行过程中申请的内存大于系统能够提供的内存,导致无法申请到足够的内存,于是就发生了内存溢出。内存泄漏的根本原因是。

流楚丶格念的博客 2106

JVM学习笔记-内存泄漏异常二

在这片文章中,将讲述方法区、运行时常量池溢出和直接内存溢出两种情况

sunjian1122的博客 290

手把手解决AutoDock安装那些坑:从autogrid报错到.map文件生成(Win10/11环境)

本文详细解析了在Win10/11环境下安装AutoDock时遇到的常见问题,特别是autogrid报错和.map文件生成失败的情况。通过实战验证的解决方案,包括环境变量设置、Python版本管理、文件权限调整等,帮助用户顺利完成安装并提升分子对接研究效率。

weixin_42548888的博客 200

简单分析cglib引起的PermSize Space内存溢出

上段时间有台机器发生了 java.lang.OutOfMemoryError: PermGen space 内存溢出的异常,当时大概判断了原因后就把 MaxPermSize 配置调高后,就把问题解决了,不过空下时间后还是需要继续把review代码。 一般来说PermSize Space OOM的话,第一种可能就是方法区溢出,第二种就是运行时常量池溢出,第二种查看后基本排除掉,问题就应该出...

touchmm 1145

高分三号SAR影像超精细条带数据预处理实战:从原始数据到地理编码的完整操作指南

本文提供了高分三号SAR影像超精细条带数据预处理的完整实战指南。详细介绍了从原始数据导入、多视处理、滤波去噪、DEM准备到地理编码与辐射定标的完整流程,帮助用户利用SARscape软件将原始SAR数据转化为可用于分析的地理编码产品。

tree8的博客 578

STM32F103ZE驱动PMW3901光流模块,从SPI配置到数据读取的完整避坑指南

本文详细解析了STM32F103ZE驱动PMW3901光流模块的全过程,从SPI配置、硬件连接到数据读取与处理,提供了完整的避坑指南。重点介绍了SPI参数配置、初始化序列优化、运动数据滤波等关键技巧,帮助开发者快速实现精准的光流检测。

weixin_42631411的博客 515

CGLIB代理过程,为什么会造成内存泄漏

写在前言:本文适合有一定基础的同学阅读,直接贴图一目了然:

weixin_40335588的博客 794

三电平逆变器LC滤波设计避坑指南:从SVPWM谐波到Simulink仿真验证

本文详细解析了三电平逆变器LC滤波设计中的关键问题,从SVPWM谐波特性到Simulink仿真验证,提供了800V/10kW系统的参数计算方法和优化技巧。通过实际案例和仿真配置示例,帮助工程师避免常见设计误区,提升电能质量和系统稳定性。

weixin_29261711的博客 173

Java内存泄漏深度分析与解决方案全景指南

内存泄漏是Java开发中的"慢性杀手",它不会立即导致系统崩溃,却会逐渐侵蚀应用性能,最终引发灾难性的OutOfMemoryError。本文将系统性地剖析Java内存泄漏的根源、检测方法和解决方案,并通过图表直观展示关键概念和流程。

一起探索IT的世界 1647

24位ADC的精度陷阱:为什么你的ADS1232实测性能不达标?

本文深入探讨了24位ADC芯片ADS1232在实际应用中性能不达标的原因,揭示了高精度ADC性能依赖周边电路设计的关键问题。通过分析电源系统、参考电压稳定性、PCB布局等常见陷阱,提供了优化方案和调试技巧,帮助工程师充分发挥ADS1232的潜力,实现接近理论值的测量精度。

weixin_28285943的博客 407

从零配置XDMA:手把手教你PCIe BAR与AXI总线的地址转换(基于Vivado2023)

本文基于Vivado2023,手把手教你配置XDMA IP核,深入解析PCIe BAR与AXI总线地址转换的核心原理。通过实战步骤,详细说明如何正确设置BAR参数(如32/64位、预取使能)及地址映射,确保主机能稳定访问FPGA侧寄存器与内存,是解决PCIe通信调试难题的关键指南。

2w3e4r5t6y的博客 1216

终极Java内存泄漏排查指南:从ThreadLocal陷阱到元空间OOM实战案例

在Java开发中,内存泄漏是导致应用性能下降、系统崩溃的隐形杀手。本文将带你深入理解CodeGuide项目中的内存泄漏典型场景,掌握从ThreadLocal使用不当到元空间溢出的完整排查与解决方法,让你的应用远离OOM风险。 ## 一、Java内存模型与泄漏风险区 Java内存管理是开发者必备技能,理解JVM内存结构是排查内存泄漏的基础。JDK 1.8及以上版本的内存模型主要分为堆、元空间和栈

gitblog_00344的博客 579

绕过App某加密企业版加固Frida检测

可以发现dlsym函数加载了两次pthread_create,可以认为这两个进程(或某一个进程)是和Frida的检测有关的,于是为了绕过检测,我们可以让它加载一个虚假的pthread_create函数,得到一个空结果绕过检测。成功绕过了libnllvm1717052779924.so的检测,又到了libface_nllvm.so的检测,使用同样的方法去绕过libface_nllvm.so的检测,代码我这里就不上了,大家可以自己修改以下。dlsym()函数的作用是在已经加载的动态链接库中查找指定符号的地址。

墨痕诉清风的博客 1624

类加载问题与内存泄漏排查:隐藏在元数据区的致命陷阱

摘要: Java类加载机制引发的内存泄漏是隐蔽但致命的问题,尤其在热部署、动态代理等场景下,未回收的ClassLoader会导致加载的类及静态变量常驻内存,最终引发Metaspace/PermGen OOM。文章深入分析四大典型泄漏场景(线程上下文ClassLoader、静态变量强引用、动态代理类爆炸、Web容器热部署),并提供Arthas、MAT等工具的实战排查方案。关键解决方案包括:规范TCCL使用、弱引用缓存、代理类复用池,以及配置JVM参数监控Metaspace。架构师需遵循生命周期对称、引用链最短

qq_43414012的博客 1121

看完就会!手把手教您如何正确开启飞牛Nas 虚拟机的 IOMMU 硬件直通,让虚拟机的性能飙升!

看完就会!手把手教您如何正确开启飞牛Nas 虚拟机的 IOMMU 硬件直通,让虚拟机的性能飙升!

XiaoqiangClub的博客 1万+

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Class加载器-内存泄漏问题的探讨2

严重警告: 如果存在任何来自应用以外的引用,引用了由J2EE应用本身类加载器加载的类和类实例对象,那么ClassLoader类加载内存泄漏问题就出现了。 ClassLoader 类加载内存泄漏问题的定位 正如上所述Classloader类加载内存泄漏是由于不断重复地启动、停止应用导致Class多次加载,并由于系统一级对象引用的关系,导致以前创建的类无法被回收,从而导致内存泄漏。 定位C...

啊松 452

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Spring Boot应用内存泄漏实战:如何用MAT揪出那个偷偷吃内存的ApplicationContext

本文详细介绍了如何利用MAT工具诊断Spring Boot应用中的内存泄漏问题,特别是由静态持有的ApplicationContext引发的内存泄漏。通过实战案例,展示了从症状识别到应急处理,再到使用MAT进行深度分析的完整流程,并提供了多种解决方案和防御性编程建议,帮助开发者有效预防和解决内存泄漏问题。

transformer2023的博客 614

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Spring应用内存泄漏实战:如何用MAT揪出那个偷偷吃内存的ApplicationContext

本文通过一个真实的线上案例,详细介绍了Spring应用因ApplicationContext被静态持有而导致内存泄漏的排查过程。文章系统讲解了如何利用jmap生成堆转储,并借助MAT工具进行深度分析,最终定位并修复问题根源,为开发者提供了一套完整的Spring内存泄漏排查与解决方案。

ll5678的博客 686

Java内存泄漏诊断:Arthas+Heapdump+MAT实战指南

内存泄漏是Java应用中常见的性能问题,会导致应用逐渐耗尽内存最终崩溃。其原理是对象因错误引用无法被GC回收,随着时间推移不断累积。通过JVM堆内存分析技术,开发者可以精准定位泄漏点,保障系统稳定性。本文介绍的Arthas+Heapdump+MAT工具链,实现了无需重启服务的生产级内存诊断方案。其中Arthas提供动态获取内存快照能力,Heapdump生成标准化的堆转储文件,MAT则进行可视化分析。这套方法特别适用于金融等高可用性要求的场景,能有效应对静态集合增长、未关闭资源等典型泄漏模式。

weixin_29597551的博客 329

OpenFeign - 内存泄漏风险:Feign 客户端生命周期管理建议

OpenFeign 内存泄漏风险与生命周期管理建议 本文深入分析了OpenFeign在微服务架构中可能引发的内存泄漏问题。通过剖析内存泄漏的形成机制,指出OpenFeign客户端因动态代理特性可能长期持有资源对象而无法被GC回收。文章重点阐述了OpenFeign客户端的四个生命周期阶段(创建、初始化、使用和销毁)及管理要点,并通过Mermaid图表直观展示其完整生命周期流程。 针对两种典型内存泄漏场景提供了详细案例分析:一是错误地在请求中频繁创建新实例导致资源累积;二是通过静态变量或循环引用长期持有客户端实

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

从Groovy到Spring:那些容易引发Metaspace溢出的隐藏陷阱(含解决方案)

本文深入探讨了Groovy脚本引擎与Spring框架组合中容易引发Metaspace溢出的隐藏陷阱,包括动态类爆炸、反射工厂滥用和类加载器泄漏等问题。通过分析JVM的Metaspace内存机制,提供了优化Groovy脚本执行、Spring配置调整以及诊断与应急方案,帮助开发者有效预防和解决OutOfMemoryError问题。

weixin_30632089的博客 480

为什么你的Java服务频繁OOM?:90%开发者忽略的内存泄漏陷阱

掌握Java内存泄漏排查方法,有效解决服务频繁OOM难题。适用于高并发、长时间运行的Java应用,通过堆转储分析、引用链追踪定位泄漏源,提升系统稳定性。实用技巧值得收藏。

ProceSeed的博客 473

定时任务引发的JVM内存泄漏:从OOM到静态Map根因排查实战

在Java后端服务中,JVM内存管理是保障应用稳定运行的基石,而OutOfMemoryError(OOM)则是开发者最常遇到的致命故障之一。很多情况下,OOM并非源于堆内存配置不足,而是代码中隐藏的内存泄漏问题——例如静态集合无限增长、缓存无上限、异常路径处理不当等。当GC频繁触发却回收不到空间,老年代被持续占满,服务便会陷入假死状态。这类问题在定时任务场景中尤为常见:批量扫描、状态缓存、重试机制叠加,极易形成“慢性内存泄漏”。通过堆转储分析与GC Roots追踪,可以快速定位到持有大量对象的根因引用。本文

weixin_33585822的博客 285

MetaSpace 动态生成代理类泄漏的 Dump 分析实录

或者框架在使用动态代理时。

dicky_zhang3的博客 1904

Metaspace内存泄漏频发?你可能忽略了这3个Class卸载必要条件

解决Metaspace内存泄漏难题,关键在于掌握Metaspace的Class卸载条件。本文详解三大必要条件:类加载器被回收、无活跃实例引用、类数据无外部引用,适用于JVM调优与线上故障排查。理解这些机制可显著提升应用稳定性,避免频繁Full GC,值得收藏。

CompiShoal的博客 675
上一篇: myeclipse8.5 安装findbugs插件
下一篇: JSP页面打印
zxycode007
博客等级 码龄18年 53粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值