【Java精品源码栏目提醒】:文章导读:在新的一年中,各位网友都进入紧张的学习或是工作阶段。
网学会员整理了Java精品源码-Java内存泄露模拟及分析解决方法 - 其它资料的相关内容供大家参考,祝大家在新的一年里工作和学习顺利!
e-mail:derweeqq.com derwee
Java 内存泄露模拟及分析解决方法1.1 实践目标: 1、使用
JAVA 代码实现模拟内存溢出 2、分析 JDK 内存溢出的原因 3、总结存在 bug 的
JAVA 编码实践 4、总结 JVM 优化的方法1.2 模拟内存溢出: 为了方便模拟内存,特意把 JVM 的内存参数指定为更小(我的本本内存是 8G 的)。
修改 eclipse 参数文件 eclipse.ini 调用 JVM 参数: -vmargs -Xms40m(原始是-Xms40m) -Xmx100m(原始是-Xmx384m) 演示
JAVA 小程序实现原理:使用集合类对象装载大量的 Persion 对象,每次把 new 出来的对象加入集合类对象后,更改对象的属性,再从集合类对象中删除该对象。
会出现该删除的对象没有被删掉,Persion 类对象不断占用内存,导致分配给 JVM 的内存被耗光。
package com.derwee.collection.memoryimport
java.util./ ClassName: OutOfMemory Description: 内存溢出模拟,提出解决方法 author yangdw date 2012-3-25 下午6:58:49/public class OutOfMemory public static void mainString args Collection collection new HashSet forint i0i JVM 堆 空 间 的 极 限 值 。
就 会 出 现
java.lang.OutOfMemoryError:
Java heap space。
解决方法: 1、 评估应用程序需要内存大小。
比如大量大对象存储在集合对象内, 迟迟不处理。
应该及时释放稀有资源 2、 释放方式不合理,以为释放了,其实没有,如上面的演示程序。
上面的演示程 序中,因为使用了以 hashcode 来定位查找的 hashset但对象的 name 参与计算 hashcode 的值被改变了,最后得到的 hashcode 变了, 并没有把该删的对象删除(collection.removeper //把 刚加到集 合里的对 象 ,集合对象还引用着每一个刚新生成的对象,即使手工调用垃圾回收 删除) (System.gc//手工调用垃圾回收器)也是没有用的。
因为手工调用 gc 方法仅 仅是通知 JVM 而已,至于 JVM 垃圾回收线程有没有空,能不能成功回收,gc 也管不了。
集合对象长度(collection.size )说明了内存溢出。
1.4 大型工程中内存溢出排查建议1 如果在大型工程里排查内存溢出,Jconsole 已经不是最优的工具了。
因为 Jconsole 没有提供细到对象级粒度的监控。
只能根据应用程序正常状态下耗用内存量,和在异常 状态下耗用内存量比较,可以明确是否存在内存溢出的情况存在。
如果已经确定待处理 的大型应用程序存在内存溢出问题,要定位到某个
Java 对象,某段
Java 逻辑代码,位 于某个
Java 文件,可以借助 JProfiler 进行对象级的跟踪,接着定位到相关逻辑代码块。
如果这部分的工作给非常熟悉这个应用程序的人排查,应该可以比较快找出存在逻辑问 题的代码块。
1.5 存在 bug 的代码实践: 其实造成内存溢出,除了物理条件外,往往是代码 bug。
上面实例是 public inthashCode造成的内存溢出,可见这个方法很神奇。
在编写代码时往往重写两个方法 hashCode 和equals。
当在集合类中如 HashSet, hashCode: 要特别注意 hashCode Hashtable 中存储对象时, HashMap,的实现,如果得到的哈希码变了,在哈希集合中是无法定位该对象的,出现删除该对象失败的情况,最终导致了内存溢出了。
equals:如果要比较对象的内容,请重写该方法,因为默认的
java.lang.Object.equals实现的是比较对象的内存地址。
下面代码说明了 StringBuffer 实现没有重写 equals 方法,但 String重写了 equals 方法如:Set companys new HashSet companys.addnew StringBufferhttp://www.hundsun.com/ companys.addnew StringBuffer http://www.hundsun.com/ System.out.printlncompanys.size//结果是 2Set companys new HashSet companys.addnew Stringhttp://www.hundsun.com/ companys.addnew String http://www.hundsun.com/ System.out.printlncompanys.size//结果是 1 对于 HashSet 类是怎么确定一个对象是否已经存在集合类中呢?根据
Java 源码, HashSet类首先看 hashcode 方法是否相等,然后看 equals 方法是否相等。
注意:当重写对象的 hashCode 和 equals,当用到哈希集合时注意不要轻易更改参与哈希码运算的对象属性,如 person 的 id 和 name. 1.5.1 equals 和 hashcode 重写原则21.5.1.1 对 equals应该遵循如下要求 1 对称性:如果 x.equalsy返回是“true”,那么 y.equalsx也应该返回 是“true”。
2 自反性:x.equalsx必须返回是“true”。
3 传递性: 是“true”而且 y.equalsz返 是 如果 x.equalsy返回 , 回 “true”, 那么 z.equalsx也应该返回是“true”。
4 任何情况下,x.equalsnull,永远返回是“false”。
5 x.equals和 x 不同类型的对象永远返回是“false”。
1.5.1.2 hashCode的返回值和 equals的关系如下 1 如果 x.equalsy返回“true”,那么 x 和 y 的 hashCode必须相等。
2 如果 x.equalsy返回“false”,那么 x 和 y 的 hashCode有可能相等, 也有可能不等。
1.6 基于 JVM 优化 开始使用 JDK 默认参数,使用 JDK 自带的 jconsole 连续观察内存使用情况,线程 数,确定基线后,设置合理的优化参数。
根据实际情况定制合适的优化参数,但不要盲 目设置 JVM 的优化参数,可能会触发 JDK 未知 BUG! -Xms:启动应用时,JVM 堆空间的初始大小值。
-Xmx: 应用运行中,JVM 堆空间的极限值。
为了不消耗扩大 JVM 堆控件分配的开销, 往往此参数和-Xms 这个两个值设为相等。
NewSize:堆中新对象区大小。
PermSize 设置非堆内存初始值,默认是物理内存的 1/64。
MaxPermSize:应用运行中,永久存储区的极限值。
如:set
JAVA_OPTS-Xms800m -Xmx800m -XX:PermSize128M -XX:MaxNewSize256m-XX:MaxPermSize256m优化原则:1、 在 JVM 中如果 98%的时间用于 GC 且可用的 Heap size 不足 2%的时候将抛出此异常信 息。
2、 Heap Size 最大不要超过可用物理内存的 80%,一般的要将-Xms 和-Xmx 选项设置为相 同,而-Xmn 为 1/4 的-Xmx 值。
3、 JVM 初始分配的内存由-Xms 指定, JVM 最大分配的内存由-Xmx 默认是物理内存的 1/64; 指定,默认是物理内存的 1/4。
4、 默认空余堆内存小于 40时,JVM 就会增大堆直到-Xmx 的最大限制;空余堆内存大于 70时,JVM 会减少堆直到-Xms 的最小限制。
因此服务器一般设置-Xms、-Xmx 相等 以避免在每次 GC 后调整堆的大小。
5、 内存分配大小 2GB-3GB (一般来说 Windows 系统下为 1.5G-2G,Linux 系统下为 2G-3G), 而 64bit 以上的处理器就不会有限制了。
1.6.1 JVM 监控工具介绍 JCONSOLE 可以观察到
java 进程的 gc, 这是随着 JDK 分发的一个图形化 JVM 监控工具, class, 内存等信息。
在主界面中有 6 大 tab 页: 1. 概述:有关堆内存使用情况,线程,类加载和 CPU 使用情况的总体情况,支 持时间范围查询 2. 内存:内存的详细情况,堆和其他内存,支持时间粒度查询 3. 线程:峰值/活动线程、各个线程的明细信息、检测死锁 4. 类:监控加载和卸载的类 5. vm 摘要:有关 vm 的明细信息 6. MBean:当前
Java 程序的 MBean 的操作 支持本地 JVM 和远程 JVM 监控。
JProfiler JProfiler 是一个全功能的
Java 剖析工具,主要用于检查和跟踪系统(限于
Java 开 发的)的性能。
JProfiler 可以通过时时的监控系统的内存使用情况,随时监视垃圾回收,线程运行 状况等手段,从而很好的监视 JVM 运行情况及其性能。
它把 CPU、线程和内存的剖析 组合在一个强大的应用中。
一般场合,如果只是监控
Java 应用程序的总体性能情况,和判断 JVM 相关参数是 否设置合理, 但如果要监控对象级这种粒度的问题, 使用 Jconsole 即可。
需要使用 JProfiler 才能完成该任务了。
备注: 1 参考符圣劝老师建议 2 参考符圣劝老师建议