定位和优化隐藏的内存问题
来源:互联网 发布:淘宝怎么删除差评 编辑:程序博客网 时间:2024/04/30 19:44
问题阐述
现象、问题描述
Java程序除了内存泄漏外,也会存在其他一些内存问题,比如: 假内存泄漏,随机内存泄漏,垃圾对象产生过多过快,大对象的生成等.
内存泄漏问题主要表现在随时间增加(有时候可能需要上月上年的时间)而内存使用增加;而假内存泄漏也可能出现上面的现象,最终出现 OutOfMemoryException,但是当增大堆内存后,又不一定会出现;随即内存泄漏又可以称为隐藏内存泄漏,其产生的时机, 以及其危害成都都是不确定的.
垃圾对象产生过多过快,并没有衡量标准,但是和预计的值相差过大.比如一个流程呼叫产生60K的对象就不正常了.大对象的生成也跟此相关,但是主要还是程序运行不稳定, GC频繁, AF GC过多.
上面这些问题, 在UC项目中都出现过,而且较难定位.
问题检测、根本原因分析
这些问题, 难点在与定位, 解决办法一般都很简单.因此掌握定位方法是解决上述问题的关键.这里我们通过一步一步的介绍来解决上面的问题.
一,垃圾对象问题
首先我们需要解决垃圾对象生成过多过快的问题.由于内存泄漏一般都会在高压力情况下才能较快出现,所以就无法有效的使用 OptimizeIT工具;在低呼叫量下,垃圾对象又会对其产生消极影响.于是我们必须先解决一些不必要的对象问题,降低对象相互影响因素, 保证在低呼叫量的情况下也可以使用该工具进行内存分析.对于这个问题,我们可以按照如下步骤操作:
1,使用 OptimizeIT工具的 Profile模式启动系统, 然后连接上 OptimizeIT工具.
2,连接上一个运行的系统后,则 OptimizeIT会检测系统运行的堆内存使用状况, 按照“Diff.”排序就可以清晰的看到哪些对象增加过快了.
3,观测看那个对象增长比较快,然后查看该对象的分配情况,本例子中, String对象分配比较快,数据增加也比较多,于是先观测此对象:
注意: 根据经验, 一般来说都是由于 String对象增加过多导致的系统堆内存消耗过快,因此建议一开始就对 String 对象进行分析.
4,在对象分配图中,首先找到系统本身的类,比如这里就是TestMemoryOne.call():
注意: 这里首先选择的是系统本身的类, 而不是所在比例最好的类.因为只能优化本身类,所以建议先通过对象分配比例排序,然后一个一个的找出该系统的类,然后进行分析.
5,找到本身系统类的分配情况后,查看是哪个方法分配的,然后定位到该方法, 分析源代码,确保该方法中,对象使用没有问题:
public void call()
{
log.debug("a" + "b" + "c" + this.toString());
}
本案例就是因为没有判断日志级别的可用性,而导致垃圾对象生成.作如下修改后, String对象就变得很少了. UC项目就是因为在代码中没有添加日志级别的判断,导致发送消息的方法中生成大量垃圾对象,而影响性能.
public void call()
{
if(log.isDebugEnabled())
{
log.debug("a" + "b" + "c" + this.toString());
}
}
另外我们还发现 HeapCharBuffer对象也分配过快,于是查看 main()方法,我们就可以找到在一个循环中,有调用System.out.println()的地方.
6,反复进行3-5步的操作,对所有增加过快的对象都进行检测.避免大部分垃圾对象的生成.
大对象问题
大对象问题比较容易定位,其方法跟先前定位垃圾对象差不多.对于大对象问题, 一般来说都是找方法规避,并不能真正解决.
1. 首先还在对内存使用视图中,通过“size diff…”进行排序,找到内存大小增加较大的而对象数量较小的一些对象进行分析.
2,确定一个对象后,查看其分配的情况.跟分析垃圾对象一样,找到相关的代码进行分析,确保大对象的分配是必须的. 如果存在不必要的操作,获取可以规避的操作,则可进行优化改经.
注意: 一般来说, 大对象的生成情况比较少用,所以需要仔细分析每个地方是否必要,要避免比如大对象拷贝等(UC项目就有把一个大消息组装成 String对象打印到日志中的操作).
2. 反复进行上一步的操作,对所有大对象操作都进行优化。
具体介绍
内存泄漏
分析内存泄漏, 不管是什么情况下导致的, 其分析方法都一样.由于系统的类和第三方类以及JDK的类都有可能存在内存泄漏的问题,所有都需要分析,一般来说会分别进行(通过类过滤器,过滤出分析部分).
首先我们分析自己系统的类,通过在类filter中输入“test.*”来过滤掉其他的类:
先执行一次GC,然后马上标记当前的对象实例数:
然后继续执行GC,看看其“diff”是否恢复到 0或者负数.多观测几次,如果确定其不能恢复到 0或者负数,则说明其有可能存在内存泄漏问题.
注意: 这里只是说明其可能存在问题, 并不一定时问题,需要通过代码分析才能最后下结论.具体还需要参考后面的假内存泄漏和隐藏内存泄漏问题的处理.
通过查看其分配情况,找到产生该对象的类和方法,通过分析代码, 最终确定是否有内存泄漏问题.
最后在对其他类进行同样的分析,找出所有的可疑点.
假内存泄漏
在分析完代码后,可以确定的是不存在内存泄漏,那么就有可能是假内存泄漏了. 主要有一下几种情况:
1,过大的队列,导致队列中的数据还没有装满,就出现 OutOfMemoryException异常. 此时在 OptimizeIT中也可能会看到对象实例不断的增加,但是实际上代码是没有问题(队列有大小限制).
2,过多的线程(长期运行),导致内存耗尽.这个在 suse8上更是厉害, suse8上使用进程模拟线程, 每个线程都要分配2M的空间,如此计算, 1000个线程就需要2G的堆内存.
3,IO数据读取,通过临时对象保存,也可能会出现这个问题.
隐藏内存泄漏
如果某个情况, 在剔除假内存泄漏的情况下, 那么就有可能属于隐藏内存泄漏问题了.这个主要是由于跟该段代码相关的代码问题导致的,非常难定位.
比如 Tomcat曾经出现过一个关于 session对象无法回收的问题. 对于每个 session对象,当有一个 request进来时,其会有一个标记 + 1来记录当前 request数目.当 request完成时,该标记会 -1.然后在 Tomcat中 forward一个 request时,如果出现异常,则该 -1操作就不会被执行到,导致该标记永远 >0,于是该 session将永远不被回收.
定位这种问题, 主要是通过调试代码, 找到问题点,然后分析问题点的相关代码.没有太多捷径可询, 一般都是通过分析相关调用来定位.
注意: 对于这个情况, 根据经验分析,大部分都是由于异常处理不当导致的,所以需要对一些异常处理的地方进行分析.首先保证是否有异常处理; 然后确定异常处理是否合理;最后确定那些资源释放操作是否都放到了 finally块中.
经验总结、预防措施和规范建议
对于内存泄漏, 只有需要细心的一个个类的分析才能找出问题所在, 有时候还可能需要对同一个类进行多次分析. 但是这些分析是有价值的, 不但可以找出内存的问题,还可以对系统性能的提升有帮助.
对于小部分的内存泄漏问题,通过上面的方式分析后,还不能定位, 则需要寻找有经验的人员,了解系统设计,从系统架构上着手,逐步确定问题范围,最后在通过工具确定.这个只有通过不断的学习和实践才能达到,所以需要多对系统分析,掌握规律, 提高分析能力和问题认识广度.
- 定位和优化隐藏的内存问题
- 如何定位和解决Andorid的内存溢出问题
- 如何定位和解决Andorid的内存溢出问题
- 关于Tomcat内存优化和eclipse内存分配的问题
- iOS导航栏的隐藏(优化导航出现和隐藏的问题)
- cpu和内存问题定位分析
- iOS开发内存优化-问题代码定位
- linux下内存的统计和内存泄露类问题的定位
- linux下内存的统计和内存泄露类问题的定位
- linux下内存的统计和内存泄露类问题的定位
- linux下内存的统计和内存泄露类问题的定位
- linux下内存的统计和内存泄露问题的定位-转
- linux下内存的统计和内存泄露类问题的定位
- linux下内存的统计和内存泄露类问题的定位
- linux下内存的统计和内存泄露类问题的定位
- linux下内存的统计和内存泄露类问题的定位
- linux下内存的统计和内存泄露类问题的定位
- Linux下内存统计和内存泄露类问题的定位方法
- jsp+servlet+jquery 用jquery uploadify最新版本实现多文件上传带进度条
- struts2视频上传,一直进不了action
- 程序猿也爱学英语(上),有图有真相
- Apache Mesos make errors
- 黑马程序员---图形化界面(GUI)
- 定位和优化隐藏的内存问题
- 面向 Java 开发人员的 Ajax: Ajax 的 Java 对象序列化
- mysql 不区分大小写
- 一些鲜为人知却非常实用的数据结构
- java基于atomikos做分布式事务(简单实例)
- 用代码 强制设置横屏或竖屏 设置全屏
- swing中的并发
- PHP 运算符
- matplotlib,numpy自己动手安装