惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
G
Google Developers Blog
博客园 - 三生石上(FineUI控件)
B
Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hugging Face - Blog
Hugging Face - Blog
C
Check Point Blog
V
V2EX
Vercel News
Vercel News
U
Unit 42
Recent Announcements
Recent Announcements
Last Week in AI
Last Week in AI
J
Java Code Geeks
WordPress大学
WordPress大学
罗磊的独立博客
I
InfoQ
阮一峰的网络日志
阮一峰的网络日志
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
小众软件
小众软件
M
MIT News - Artificial intelligence
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Apple Machine Learning Research
Apple Machine Learning Research
Martin Fowler
Martin Fowler
云风的 BLOG
云风的 BLOG

博客园 - 巴山游子

Java内存泄漏的排查总结 第一次有人把 5G 讲的这么简单明了 关于NB-IoT,没有比这篇更通俗易懂的啦! 使用SQL Server 2014 In-Memory 内存数据库时需要注意的地方 SQL Server Profiler:使用方法和指标说明 SQL Server 维护计划实现数据库备份(策略实战) SQL Server 连接问题圣经-命名管道 如何查看 exec sp_execute 10 XXX, XXXX的RPC事件 内容 图解HTTPS 常用 T_SQL 语句 淘宝前端技术巡礼 快速找出数据库的性能问题之:缺失索引 &无用的索引 网页设计师必备的35套图标(免费下载) SQL Mon 介绍 网站设计常用技巧收集 web 学习资源整理 使用SQL 脚本 导入 Excel 数据 web 页面上的工作流设计 CodeSmith 学习资料收集
JAVA内存泄露分析及解决
巴山游子 · 2019-05-10 · via 博客园 - 巴山游子

,问题产生
    项目采用Tomcat6.0为服务器,数据库为mysql5.1,数据库持久层为hibernate3.0,以springMVC3.0为框架,项目开发完成后,上线前夕进行稳定性拷机,测试数据为插入4条/S,更新4条/S,访问300次/S,前期运行速度顺畅,三天后就开始运行缓慢,访问量达到1500W次后以抛出Java heap space结束.
.问题分析
    1.前期分析为连接池内存溢出,期间优化了连接池参数,调整了tomcat线程参数,替换数据库连接池,问题依旧
    2.看来问题不是简单的参数配置,需要进行一点深入的研究分析了,在和同事研究了一些资料后,对JAVA的垃圾收集机制有了一定的了解,JVM的内存模型分为新生代和老生代,JDK本身提供了一个监视工具jconsole.exe,进入bin文件夹打开后,选择连接--tomcat--内存,可以开始监视JVM内存回收情况,通过一段时间的监视后,发现了老生代的内存回收存在异常.

从上图可以看出,每次回收的内存都比上一次少,可以判断老生代的内存发生了泄露.
    3.虽然知道了存在内存泄露,但是无法判断是哪里发生了泄露,为此我们需要把堆(dump)导出来进行分析,JDK也提供了导出工具jvisualvm.exe,启动后右键点击线程--堆Dump,可以导出Dump文件.
    4.使用MAT(MemoryAnalyzer)分析Dump文件,该工具的下载地址为:http://www.eclipse.org/mat,可以下载离线版,也可以集成到eclipse,使用很方便.打开该工具导入Dump文件,稍等一会,就可以得出MAT提供的分析报告

MAT指出了该Dump中一个HashMap的实例发生了内存泄露,占用了JVM819M的内存,继续点击Details可以得到更详细的信息

这个detail比较详细的指出了问题所在,一个叫viewCache的hashMap实例占用共859M内存,虽然每个实例只有几百字节,但是一共产生了134W个实例.
    5.分析出这个问题点,接下去就是排查代码问题了,排查代码得知该项目使用springMVC,其中viewCache是spring中使用的一个视图缓存,在项目中如一个处理视图跳转的代码:

LinkedList list = this.getPathParam(mvValue);
    for (int i = 0; i < list.size(); i++) {
     String paramName = (String) list.get(i);
     String paramValue = null;
     paramValue = RequestUtils.getParamString(map, paramName);
     paramValue = paramValue == null ? "" : paramValue;
     mvValue = StringUtils.replace(mvValue, "#" + paramName+"#", paramValue);
   }
    return new ModelAndView(mvValue); 

由于paramValue每次都是动态生成的uuid,造成了每次的mvvalue都不同,这样每次都会生成一个不一样的视图,这样的视图累积到100多W个的时候,终于把tomcat撑暴了.百度了一下,果然也已经有人注意到了这个问题:http://jackyrong.iteye.com/blog/1744342.
    6.知道了问题所在后,就可以修改代码了,我们需要将ModelAndView的视图名称固定,动态参数可以通过ModelAndView提供的addObject方法传入,修改后的代码如下:

      //判断如果视图名称包含"snms_result.jsp",则将视图名称返回值统一,解决因动态产生视图名称过多产生的内存泄露问题, by feitianbubu 2013年5月27日 11:21:31
    ModelAndView mv=new ModelAndView(mvValue);
    LinkedList list = this.getPathParam(mvValue);
    for (int i = 0; i < list.size(); i++) {
     String paramName = (String) list.get(i);
     String paramValue = null;
     paramValue = RequestUtils.getParamString(map, paramName);
     paramValue = paramValue == null ? "" : paramValue;
     mv.addObject("id", ",'"+paramValue+"'");
    }
    return mv;

7. 代码commit SVN,发布拷机,观察内存回收情况

(为了更快模拟环境采用1000次/S插入数据和1000次/S读取数据,所以GC会比较频繁)
可以看出老生代内存得到有效回收,内存泄露的问题得到解决