Java_内存泄漏_实例1

  版权声明:本文为博主原创文章,未经博主允许不得转载。



  记一次压测时Java内存泄漏问题的发现过程(2017-08-14)

【前篇】

  ①20170811进行A系统与B系统之间的会话功能进行压测,加上脚本准备期间的聊天消息,预计累计聊天30w+条消息;

  ②20170814原计划加大量对会话功能进行压测,情况如下;

【应用表现】

  ①B系统前台打开报错“504”;

  ②查看后台应用CPU情况,CPU利用率高达700+%(8核);

  ③查看后台内存情况,持续FullGC,且一次FullGC的时长在9s左右,从这里可以粗略定位CPU高的原因是内存GC问题导致;

【查看应用JVM配置】

  ①请教B开发团队,loader提到应该不是JVM配置引起的问题;

【尝试进行分析】

  ①尝试使用jvisualvm进行“堆 dump”,但是因为没有内存了所以jvisualvm连接后卡死(之前测试可以正确连接并显示JVM情况);

  ②使用jmap命令“jmap -dump:format=b,file=heap.hprof pid”进行dump,dump文件有16G(无奈使用mat打不开);

  ③尝试shutdown应用后重启,无法shutdown;最后使用“kill -9 pid”暴力解决无法shutdown的情况,后重启应用;

【重启后情况】

  ①使用jvisualvm查看堆内存使用情况,表现为“堆内存持续上升”;

  ②重启1小时后,dump文件进行分析,其中“java.util.concurrent.LinkedBlockingQueue$Node”占用内存高达1G,基本可以判断存在“内存泄漏”;

  “com.best.oasis.B.common.entity.messageTransship.MessageTransship”对象151MB,且有160w个MessageTransship对象;

  ③B开发Review代码:原因所在:线程池中等待执行的任务队列存在内存泄漏的问题;

正常情况:

  A应用服务器发送消息给B服务器后,B服务器接收消息后将该消息存于中间表B_messagetransship中,同时将该消息转发给B客服端,B客服端接收消息并对该条消息进行ack,ack成功后删除B_messagetransship中的该条消息。为了防止消息丢失,B有一个定时重发job,用于每隔5s将B_messagetransship表中的消息再次推送一次;

异常情况:

  1.A服务器发送给B服务器的消息存在于B_messagetransship表中后(此时状态为“PENDING_SEND”),因为网络/B客户主动退出等问题,致使B客户端并未收到来自B服务器的该消息,则该消息的状态被置为“SEND_FAILED”存在表B_messagetransship中;

  2.A服务器发送给B服务器的消息,B客服端正确收到,但是B客服端发送的ACK请求返回失败,则该消息的状态被置为“PENDING_ACK”存在表B_messagetransship中;

失败消息定时重发实现逻辑:

  每隔5s从B_messagetransship中逐个取出失败的消息记录,以链式队列的形式链接在等待执行的任务队列中,若5s内该消息被线程处理且推送状态为成功,则删除数据库表中该消息记录;若5s内该消息被线程处理但推送状态为失败,则数据库表中的该条消息记录保持不变;若5s内该消息并未来得及被线程处理,下一次定时重发任务触发时,该消息会保留第二个拷贝在待处理任务队列中,以此类推;

bug发现的诱因:

  B_messagetransship表失败推送的消息量比较大,B_messagetransship表11w+条数据,失败消息量大的原因:

  ①11907条“再见”,状态为:SEND_FAILED

  产生原因:B客户端对话完毕未接收到再见语,就发起了“{"type":"close","sid":"${sid}"}”的请求,该现象在实际中也可能产生;

  ②17120条“很高兴为您服务”,状态为:PENDING_ACK

  产生原因:压测脚本未对“很高兴为您服务”消息进行ack;

  ③剩余的8w+条,为A发送给B的对话消息,推测是在脚本准备期间产生的数据;

开发下期优化思路:

  ①为B_messagetransship表中的消息增加生存时长,若超时则直接删除;

  ②限制待执行任务队列中messageTransship对象的数量,达到一定个数则不再从B_messagetransship中获取;

测试脚本修改:

  ①增加对“开始语”与“结束语”消息的ack;

时间: 2024-10-13 16:10:36

Java_内存泄漏_实例1的相关文章

Java的内存泄漏_与C/C++对比(转载总结)

原文网址:http://developer.51cto.com/art/201111/302465.htm Java内存泄露的理解与解决(1) 一般来说内存泄漏有两种情况.一种情况如在C/C++ 语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值):另一种情况则是在内存对象已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用).第一种情况,在 Java 中已经由于垃圾回收机制的引入,得到了很好的解决.所以, Java 中的内存泄漏,主要

Android内存泄漏查找和解决

Android内存泄漏查找和解决 目录: 内存泄漏的概念 一个内存泄漏的例子 Java中"失效"的private修饰符 回头看内存泄漏例子泄漏的重点 强引用与弱引用 解决内部类的内存泄漏 Context造成的泄漏 使用LeakCanary工具查找内存泄漏 总结 一.内存泄漏概念 1.什么是内存泄漏? 用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元.直到程序结束.即所谓的内存泄漏. 其实说白了就是该内存空间使用完毕之后未回收 2.内存泄漏会导致的问题 内

只运行一个实例以及内存泄漏检测

unit 使应用程序只运行一个实例; interface uses Windows; const  // - 互斥体唯一的名字  _Mutex_Name = '{19631971-1976-1981-1989-199319941995}'; var  _Mutex_Handle: THandle; implementation initialization // - 载入时调用的代码 // - 创建互斥体对象_Mutex_Handle := CreateMutex(nil, False, LPC

非静态内部类创建静态实例造成的内存泄漏

请大家思考,为什么会内存泄漏? 1. 首先,非静态内部类默认会持有外部类的引用. 2. 然后又使用了该非静态内部类创建了一个静态的实例. 3. 该静态实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收. 正确的做法有两种,一种是将内部类testResource改成静态内部类,还有就是将testResource抽取出来,封装成一个单例,如上一个例子那样,但是需要context时单例要切记注意Context的泄漏,使用ap

Android内存泄漏的本质原因、解决办法、操作实例

今年最后一个迭代终于结束了,把过程中碰到的不熟悉的东西拉出来学习总结一下 内存泄漏的本质是:[一个(巨大的)短生命周期对象的引用被一个长生命周期(异步生命周期)的对象持有] 这个东西分为两个部分 获得一个(巨大的)短生命周期的对象 这个[巨大的短生命周期的对象]在Android中最有可能的就是[Activity]了 最容易无意识获得它的方式就是[非静态内部类隐式自动持有外部类的强引用] 把这个对象赋值给了一个长生命周期的对象 这个有一些常见的套路 套路一:直接赋值给了一个类的静态成员 这个静态成

Android 系统开发_内存泄漏篇 -- "内存泄漏"的前世今生

基础了解 什么是内存泄漏? 内存泄漏是当有程序不再使用到的内存时,释放内存失败而产生了无用的内存消耗.内存泄漏并不是指物理上的内存消失,这里的内存泄漏是指由程序分配的内存,由于程序逻辑错误而导致程序失去了对该内存的控制,使得内存浪费. Java 内存分配策略 Java 程序运行时的内存分配策略有三种,分别是?静态分配?.?栈式分配?和?堆式分配?,对应的三种存储策略使用的内存空间主要分别是?静态存储区(也称方法区)?.?栈区?和?堆区?. -?静态存储区(方法区):主要存放?静态数据?.?全局

iOS开发_内存泄漏、内存溢出和野指针之间的区别

今天,在大连有一个面试,被问到了内存泄漏和野指针指向的区别,自己答的不是很好,特意回来查了资料,在博文中总结一下经验,欢迎指正. 内存泄漏:是指在堆区,alloc 或new 创建了一个对象,但是并没有放到自动释放池中,或者没有free 对象,导致这块内存一直被占用,换一种方法说,就是没有指针指向这块内存,再通俗点,开辟了一段空间,在没有被释放之前,结果找不到这块内存了,这样就会造成内存泄漏的问题.这块内存会直至程序运行结束才会被释放. 野指针:是指针指向已经delete 的对象,或者是未申请访问

(转)从内存管 理、内存泄漏、内存回收探讨C++内存管理

http://www.cr173.com/html/18898_all.html 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对 C++的痛恨,但内存管理在C++中无处不在,内存泄漏几乎在每个C++程序中都会发生,因此要想成为C++高手,内存管理一关是必须要过的,除非放弃 C++,转到Java或者.NET,他们的内存管理基本是自动的,当然你也放弃了自由和对内存的支配权,还放弃了C++超绝的性能

为什么JAVA的垃圾回收机制无法避免内存泄漏

一.本文参考: 1.<深入理解java虚拟机 JVM高级特性与最佳实践> 2.http://coderevisited.com/memory-leaks-in-java/ 二.对象已死的判定方法 要进行JVM中对象回收首先要判断对象是否已经死亡,判断的方法有如下几个: 1.引用计数法 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻 计数器为0的对象就是不可能再被使用的. 但是主流的java虚拟机里面没有选用引用计数器算法来管理内存,其