战狂粗人张

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  228 随笔 :: 0 文章 :: 12 评论 :: 20万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

准备模拟内存泄漏样例

1、定义静态变量HashMap
2、分段循环创建对象,并加入HashMap
代码如下:
复制代码
import java.util.HashMap;
import java.util.Map;
public class CyclicDependencies {
    //声明缓存对象
    private static final Map map = new HashMap();
public static void main(String args[]){
try { Thread.sleep(10000);//给打开visualvm时间 } catch (InterruptedException e) { e.printStackTrace(); } //循环添加对象到缓存 for(int i=0; i<1000000;i++){ TestMemory t = new TestMemory(); map.put("key"+i,t); } System.out.println("first");
//为dump出堆提供时间 try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } for(int i=0; i<1000000;i++){ TestMemory t = new TestMemory(); map.put("key"+i,t); } System.out.println("second");
try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } for(int i=0; i<3000000;i++){ TestMemory t = new TestMemory(); map.put("key"+i,t); } System.out.println("third");
try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } for(int i=0; i<4000000;i++){ TestMemory t = new TestMemory(); map.put("key"+i,t); } System.out.println("forth");
try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("qqqq"); } }
复制代码
 
3、配置jvm参数如下:
 -Xms512m
         -Xmx512m
         -XX:-UseGCOverheadLimit
         -XX:MaxPermSize=50m
 4、运行程序并打卡visualvm监控

使用JVisualVM分析内存泄漏

 1、查看Visual GC标签,内容如下,
这是输出first的截图:
            

 

 

这是输出forth的截图:

 

 

通过2张图对比发现:
 
老生代一直在gc,当程序继续运行可以发现老生代gc还在继续:
 
增加到了7次,但是老生代的内存并没有减少。说明存在无法被回收的对象,可能是内存泄漏了。
 
如何分析是那个对象泄漏了呢?打开抽样器标签:点击后如下图:

 

 

按照程序输出进行堆dump,当输出second时,dump一次,当输出forth时dump一次。
 
进入最后dump出来的堆标签,点击类:
 
点击右上角:“与另一个堆存储对比”。如图选择第一次导出的dump内容比较:
 
比较结果如下:
 
可以看出在两次间隔时间内TestMemory对象实例一直在增加并且多了,说明该对象引用的方法可能存在内存泄漏。
 
如何查看对象引用关系呢?
 
右键选择类TestMemory,选择“在实例视图中显示”,如下所示:
 

 

 

左侧是创建的实例总数,右侧上部为该实例的结构,下面为引用说明,从图中可以看出在类CyclicDependencies里面被引用了,并且被HashMap引用。
 
如此可以确定泄漏的位置,进而根据实际情况进行分析解决。
 

JVisualVM 远程监控 Tomcat

     1、修改远程tomcat的catalina.sh配置文件,在其中增加:
JAVA_OPTS="$JAVA_OPTS
-Djava.rmi.server.hostname=192.168.122.128
-Dcom.sun.management.jmxremote.port=18999
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
      这次配置先不走权限校验。只是打开jmx端口。
 
      2、打开jvisualvm,右键远程,选择添加远程主机:
          
      3、输入主机的名称,直接写ip,如下:
          
        右键新建的主机,选择添加JMX连接,输入在tomcat中配置的端口即可。
      4、双击打开。完毕!
 
总结:
线程死锁侦测:
jvm优化建议
     本质上是减少GC的次数。
如果是频繁创建对象的应用,可以适当增加新生代大小。常量较多可以增加持久代大小。对于单例较多的对象可以增加老生代大小。比如spring应用中。
GC选择,在JDK5.0以后,JVM会根据当前系统配置进行判断。一般执行-Server命令便可以。gc包括三种策略:串行,并行,并发。
吞吐量大大应用,一般采用并行收集,开启多个线程,加快gc的是否。
响应速度高的应用,一般采用并发收集,比如应用服务器。
     年老代建议配置为并发收集器,由于并发收集器不会压缩和整理磁盘碎片,因此建议配置:
 
 -XX:+UseConcMarkSweepGC  #并发收集年老代
     -XX:CMSInitiatingOccupancyFraction=80 # 表示年老代空间到80%时就开始执行CMS
     -XX:+UseCMSCompactAtFullCollection # 打开对年老代的压缩。可能会影响性能,但是可以消除内存碎片。 
     -XX:CMSFullGCsBeforeCompaction=10 # 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此参数设置运行次

FullGC以后对内存空间进行压缩、整理。 
直接运行linux上的jvisualvm
     下载X-Manager,可以将试图展现在本地机器上。
不受此jvm支持
     保证jvisualvm所属jdk版本和linux上一致。
posted on   战狂粗人张  阅读(200)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示