实战 - 分析java项目线上内存泄漏,netty框架工作原理

  • 发现启动后会频繁GC,最后导致OOM(OutOfMemoryError)

*/

public class T15_FullGC_Problem01 {

private static class CardInfo {

    BigDecimal price = new BigDecimal(0.0);

    String name = "张三";

    int age = 5;



    Date birthdate = new Date();



    public void m() {}

}



private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,

        new ThreadPoolExecutor.DiscardOldestPolicy());



/**

 * main方法

 * @param args

 * @throws Exception

 */

public static void main(String[] args) throws Exception {

    executor.setMaximumPoolSize(50);

    // 为什么是死循环?因为在生产环境中会有源源不断的数据需要处理,我们无法模拟线上环境, 所以用死循环代替;

    for (;;){

        modelFit();

        Thread.sleep(100);

    }

}



private static void modelFit(){

    List<CardInfo> taskList = getAllCardInfo();

    taskList.forEach(info -> {

        // do something

        executor.scheduleWithFixedDelay(() -> {

            //do sth with info

            info.m();



        }, 2, 3, TimeUnit.SECONDS);

    });

}



private static List<CardInfo> getAllCardInfo(){

    List<CardInfo> taskList = new ArrayList<>();



    for (int i = 0; i < 100; i++) {

        CardInfo ci = new CardInfo();

        taskList.add(ci);

    }



    return taskList;

}

}




[](https://gitee.com/vip204888/java-p7)启动

=================================================================



启动时加入参数`-Xms200M -Xmx200M -XX:+UseParallelGC -XX:+PrintGC -XX:+HeapDumpOnOutOfMemoryError`,这段代码运行后,老年代的内存占用会慢慢升高,待内存占用到达顶峰时,会频繁Full GC(回收老年代垃圾),直到撑爆内存,最后会导致OOM异常:`Exception in thread "pool-1-thread-1" java.lang.OutOfMemoryError: GC overhead limit exceeded`;也就是内存溢出;



[](https://gitee.com/vip204888/java-p7)运行监控

===================================================================



运行一段时间后,可以看到一直不停地full GC,并且有些线程的内存已经溢出报出OOM错误;  

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210719165645134.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI3MTg0NDk3,size_16,color_FFFFFF,t_70)



[](https://gitee.com/vip204888/java-p7)解决方案:使用arthas

============================================================================



在用arthas 的`dashboard`命令看一下内存使用情况,这一看,我的天,老年代的内存已经使用了`98.64%`,并且已经进行了`3914`次的GC,也就是说Full GC进行了`3914`次清理都没清掉那些垃圾;  

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210719170338383.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI3MTg0NDk3,size_16,color_FFFFFF,t_70)  

到这时候我们就已经确定发现了内存泄漏,接下来的工作就是要找到是那些顽固的对象没被清理调,然后在做出相应的调整;



首先要分析堆内存有哪些对象,这里使用到一个arthas的工具:`heapdump`,这个命令类似jdk的jmap,使用arthas导出堆转储文件命令;



[arthas@28747]$ heapdump --live /Users/mac/Downloads/dump.hprof

Dumping heap to /Users/mac/Downloads/dump.hprof …

Heap dump file created




导出后是一个二进制文件,这个文件直接打开看到的是乱码的,所以我们需要借助一些工具,这边有2个选择,用`jhat` 和 `jvisualVM`,因为`jhat`用的不多,所以我们用大家常用的`jvisualVM`.



使用`jvisual VM`加载堆转储文件`dump.hprof`后如下图:  

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210719165115217.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI3MTg0NDk3,size_16,color_FFFFFF,t_70)  

由此结果可以看到,`Date`对象和`Bigdecimal`对象一直无法回收,而每一个定时任务就会创建一个`Date`对象和`Bigdecimal`对象,到现在为止已经有`55万`个了,因为只增不减,撑爆内存是迟早的事。



[](https://gitee.com/vip204888/java-p7)解决方案二:使用jmap

===========================================================================



先使用`jps`命令找到正在运行的java进程id



macdeMacBook-Pro:Downloads mac$ jps

24240 App

29410 Launcher

29411 T15_FullGC_Problem01

2851

29414 Jps

29031 Main




我们运行的类为`T15_FullGC_Problem01`,对应的进程id为`29411`,记住这个进程id;



接着用`jmap`命令查看这个进程id,看看内存占用排行前十的对象有哪些:



jmap -histo:live 29411| head -10




*   `-histo:live` :表示只查看存活的对象

*   `head - 10` :这是linux自带的命令,表示查看头部前十行内容;



运行后,结果和上面的`jvisual`分析结果差不多,`Date`对象和`Bigdecimal`对象都是在`CardInfo`方法里面的。所以排行最高的就是这三个;



macdeMacBook-Pro:Downloads mac$ jmap -histo:live 29411| head -10

num #instances #bytes class name


1: 447100 32191200 java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask

2: 447126 17885040 java.math.BigDecimal

3: 447100 14307200 com.gc.T15_FullGC_Problem01$CardInfo

4: 447100 10730400 java.util.Date

5: 447100 10730400 java.util.concurrent.Executors$RunnableAdapter

6: 447100 7153600 com.gc.T15_FullGC_Problem01$$Lambda$2/664223387

7: 1 2396432 [Ljava.util.concurrent.RunnableScheduledFuture;





# 总结

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。

架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份**Java成长笔记**你需要阅读阅读,希望能够对你的职业发展有所帮助。

**[资料领取方式:戳这里免费下载](https://gitee.com/vip204888/java-p7)**

![image](https://img-blog.csdnimg.cn/img_convert/2dc6fdb23d4a41dd62e8aa5aea6255f0.png)

之后的焦虑的,毕竟好的架构师并不多。

架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份**Java成长笔记**你需要阅读阅读,希望能够对你的职业发展有所帮助。

**[资料领取方式:戳这里免费下载](https://gitee.com/vip204888/java-p7)**

[外链图片转存中...(img-wd6bM30Z-1628440445306)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值