专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Java教程 » 内存泄漏:JAVA内存泄漏 走开! »正文

内存泄漏:JAVA内存泄漏 走开!

来源: 发布时间:星期一, 2009年3月16日 浏览:0次 评论:0
摘要  尽管java虚拟机和垃圾回收机制管理着大部分内存事务但是在java软件Software中还是可能存在内存泄漏情况在大型工程中内存泄漏是个普遍问题避免内存泄漏就是要了解他们发生原因这篇文章就是要介绍些常见缺陷然后提供些非常好实战例子来指导你写出没有内存泄漏代码旦你存在内存泄漏要查明代码中引起泄漏原因是很困难同时这篇文章也要介绍个新工具来查找内存泄漏然后指明发生根本原因这个工具容易上手可以让你找到产品级系统中内存泄漏 垃圾回收(GC)角色  虽然垃圾回收关心着大部分问题包括内存管理使得任务显得更加轻松但是员还是可能犯些导致内存泄漏问题GC(垃圾回收)通过递归对所有从“根”对象(堆栈中对象静态数据成员JNI句柄等等)继承下来引用进行工作然后标记所有可以访问活动着对象而这些对象变成了能够操纵对象其他对象都被释放了GC使得不能够访问那些被释放对象所以这样做是安全   内存管理可以说是自动但是这并没有让员脱离内存管理问题比方说对于内存分配(还有释放)总是存在开销尽管这些开销对员来说是隐含如果创建了很多对象那么它就要比完成相同任务而创建了较少对象执行速度慢(如果其他条件都相同)  文章更多想说导致内存泄漏主要原因是先前申请了内存空间而忘记了释放如果中存在对无用对象引用那么这些对象就会驻留内存消耗内存无法让垃圾回收器验证这些对象是否不再需要正如我们前面看到如果存在对象引用这个对象就被定义为“活动同时不会被释放要确定对象所占内存将被回收员就要务必确认该对象不再会被使用典型做法就是把对象数据成员设为null或者从集合中移除该对象注意当局部变量不需要时不需明显设为null个思路方法执行完毕时这些引用会自动被清理   从更高个层次看这就是所有存在内存管语言对内存泄漏所考虑事情剩余对象引用将不再会被使用 典型泄漏  既然我们知道了在java中确实会存在内存泄漏那么就让我们看些典型泄漏并找出他们发生原因全局集合  在大型应用中存在各种各样全局数据仓库是很普遍比如个JNDI-tree或者个session table在这些情况下注意力就被放在了管理数据仓库大小上当然是有些适当机制可以将仓库中无用数据移除  可以有很多区别解决形式其中最常用种周期运行清除作业这个作业会验证仓库中数据然后清除切不需要数据  另个办法是计算引用数量集合负责跟踪集合中每个元素引用者数量这要求引用者通知集合什么时候已经对元素处理完毕当引用者数目为零时就可以移除集合中相关元素 高速缓存Cache  高速缓存Cache是种用来快速查找已经执行过操作结果数据结构因此如果个操作执行很慢你可以先把普通输入数据放入高速缓存Cache然后过些时间再高速缓存Cache中数据高速缓存Cache多少还有点动态实现意思当数据操作完毕又被送入高速缓存Cache个典型算法如下所示:  1. 检查结果是否在高速缓存Cache中存在则返回结果;  2. 如果结果不在那么计算结果;  3. 将结果放入高速缓存Cache以备将来操作  这个算法问题(或者说潜在内存泄漏)在最后如果操作是分别多次输入那么存入高速缓存Cache内容将会非常大很明显这个思路方法不可取   为了避免这种潜在致命设计就必须确定高速缓存Cache在他所使用内存中有个上界因此更好算法是:  1. 检查结果是否在高速缓存Cache中存在则返回结果;  2. 如果结果不在那么计算结果;  3. 如果高速缓存Cache所占空间过大移除缓存Cache中旧结果;  4. 将结果放入高速缓存Cache以备将来操作   通过不断从缓存Cache中移除旧结果我们可以假设将来最新输入数据可能被重用几率要远远大于旧结果这通常是个不错设想  这个新算法会确保高速缓存Cache容量在预先确定范围内精确范围是很难计算缓存Cache中对象存在引用时将继续有效正确划分高速缓存Cache大小是个复杂任务你必须权衡可使用内存大小和数据快速存取的间矛盾  另个解决这个问题途径是使用java.lang.ref.SoftReference类来将对象放入高速缓存Cache这个思路方法可以保证当虚拟机用完内存或者需要更多堆时候可以释放这些对象引用 类装载器  Java类装载器创建就存在很多导致内存泄漏漏洞由于类装载器复杂结构使得很难得到内存泄漏透视图这些困难不仅仅是由于类装载器只和“普通”对象引用有关同时也和对象内部引用有关比如数据变量思路方法和各种类这意味着只要存在对数据变量思路方法各种类和对象类装载器那么类装载器将驻留在JVM中既然类装载器可以同很多类关联同时也可以和静态数据变量关联那么相当多内存就可能发生泄漏定位内存泄漏  常常地内存泄漏最初迹象发生在出错的后在你中得到个OutOfMemoryError这种典型情况发生在产品环境中而在那里你希望内存泄漏尽可能调试可能性也达到最小也许你测试环境和产品系统环境不尽相同导致泄露只会在产品中暴露这种情况下你需要个低负荷工具来监听和寻找内存泄漏同时你还需要把这个工具同你系统联系起来而不需要重新启动他或者机械化你代码也许更重要当你做分析时候你需要能够同工具分离而使得系统不会受到干扰   个OutOfMemoryError常常是内存泄漏个标志有可能应用确用了太多内存;这个时候你既不能增加JVM数量也不能改变你而使得他减少内存使用但是在大多数情况下个OutOfMemoryError是内存泄漏标志个解决办法就是继续监听GC活动看看随时间流逝内存使用量是否会增加如果有定存在内存泄漏 详细输出  有很多办法来监听垃圾回收器活动也许运用最广泛就是以:-Xverbose:gc选项运行JVM然后观察输出结果段时间   [memory] 10.109-10.235: GC 65536K->16788K (65536K), 126.000 ms   箭头后值(在这个例子中 16788K)是垃圾回收后堆使用量 控制台  观察这些无尽GC详细统计输出是件非常单调乏味事情好在有些工具来代替我们做这些事情The JRockit Management Console可以用图形方式输出堆使用量通过观察图像我们可以很方便观察堆使用量是否伴随时间增长

+F/Fe)sj"K Figure 1. The JRockit Management ConsoleJAVA中文站社区门户3`5|I;Q h/b%QG

  管理控制台甚至可以配置成在堆使用量出现问题(或者其他事件发生)时向你发送邮件这个显然使得监控内存泄漏更加容易 内存泄漏探测工具  有很多专门内存泄漏探测工具其中The JRockit Memory Leak Detector可以供来观察内存泄漏也可以针对性地找到泄漏原因这个强大工具被紧密地集成在JRockit JVM中可以提供最低可能内存事务也可以轻松访问虚拟机专门工具优势  旦你知道中存在内存泄漏你需要更专业工具来查明为什么这里会有泄漏而JVM是不可能告诉你现在有很多工具可以利用了这些工具本质上主要通过两种思路方法来得到JVM存储系统信息:JVMTI和字节码仪器Java虚拟机工具接口(JVMTI)和他原有形式JVMPI(压型接口profiling Interface)都是标准接口作为外部工具同JVM进行通信搜集JVM信息字节码仪器则是引用通过探针获得工具所需字节信息预处理技术   通过这些技术来侦测内存泄漏存在两个缺点而这使得他们在产品级环境中运用不够理想首先根据两者对内存使用量和内存事务性能降级是不可以忽略从JVM获得使用量信息需要在工具中导出收集和处理这意味着要分配内存按照JVM性能导出信息是需要开销垃圾回收器在搜集信息时候是运行非常缓慢个缺点就是这些工具所需要信息是关系到JVM让工具在JVM开始运行时候和它关联而在分析时候分离工具而保持JVM运行这显然是不可能   既然JRockit Memory Leak Detector是被集成到JVM中那么以上两种缺点就不再存在首先大部分处理和分析都是在JVM中完成所以就不再需要传送或重建任何数据处理也可以建立在垃圾回收器基础上即提高速度再有内存泄漏侦测器可以同个运行JVM关联和分离只要JVM在开始时候伴随着 –Xmanagement选项(通过远程JMX接口允许监听和管理JVM)当工具分离以后工具不会遗留任何东西在JVM中;JVM就可以全速运行代码就好像工具关联的前 趋势分析  让我们更深步来观察这个工具了解他如何捕捉到内存泄漏在你了解到代码中存在内存泄漏步就是尝试计算出什么数据在泄漏——哪个对象类导致泄露The JRockit Memory Leak Detector通过在垃圾回收时候计算每个类所包含现有对象来达到目如果某个类对象成员数目随着时间增长(增长率)那么这里很可能存在泄漏 JAVA中文站社区门户'Yn4tm*eqj2c.C4y

Figure 2. The trend analysis view of the Memory Leak DetectorJAVA中文站社区门户7]&Zv D$K0~h7uE(w4S

  个泄漏很可能只是像水滴样小所以趋势分析必须运行足够长段时间在每个短暂时间段里局部类增加会使得泄漏发生推迟但是内存事务是非常小(最大内存事务是由在每个垃圾回收时从JRockit向内存泄漏探测器发送个数据包组成)内存事务不应该成为任何系统问题——甚至个在产品阶段全速运行开始数字会有很大跳转随时间推进这些数字会变得稳定而后显示哪些类会不断增大寻找根本原因  知道那些对象类会导致泄露有时候足够制止泄露问题这个类也许只是被用在非常有限部分通过快速视察就可以找到问题所在不幸这些信息是不够比方说经常导致内存泄漏对象类java.lang.String然而String类被应用于整个这就变得有些无助   我们想知道是其他对象是否会导致内存泄漏好比上面提到String类为什么这些导致泄漏对象还是在周围存在?哪些引用是指向这些对象?如果列出所有引用String对象工作就会变得太大而没有实际意义为了限制数据数量我们可以通过类把他们编成个组这样我们就可以看到那些其他类对象会依然泄漏对象(String类)比如个String类放入Hashtable那里我们可以看到关联到String类Hashtable入口从Hashtable入口向后运行我们终于找到那些关联到String类Hashtable对象(参看图 3如下) JAVA中文站社区门户_\$V#U6c5Uj9K

Figure 3. Sample view of the type graph as seen in the toolJAVA中文站社区门户Y X!uC7s*T

向后工作  自从开始我们就直着眼于对象类而不是单独对象我们不知道那个Hashtable存在泄漏如果我们可以找出所有Hashtable在系统中有多大我们可以假设最大那个Hashtable存在泄漏(它可以聚集足够泄漏而变得很大)因此所有Hashtable同时有和所有他们所涉及数据可以帮助我们查明导致泄露精确Hashtable JAVA中文站社区门户R NK3\v*c[@y F

Figure 4. Screenshot of the list of Hashtable objects and the size of the data they are holding live

/Yi6`p.M$d9N;\  计算个对象所涉及数据开销是非常大(这要求引用图表伴随着那个对象作为根运行)而且如果对每个对象都这样处理就需要很多时间知道些有关Hashtable内部实现机制可以带来捷径在内部个Hashtable有个Hashtable入口增长伴随着Hashtable中对象增长因此要找到最大Hashtable我们可以把搜索限制在寻找包含Hashtable引用入口最大这样就更快捷了 JAVA中文站社区门户/fX-Q~ `

Figure 5. Screenshot of the listing of the largest Hashtable entry .gif' />s, as well as their sizes.JAVA中文站社区门户&T'H5a|,vZ

向下深入  当我们发现了存在泄漏Hashtable例子就可以顺藤摸瓜找到其他引用这些Hashtable例子然后用上面思路方法来找到是那个Hashtable存在问题

%Za |-R(Y&U(N K? Figure 6. This is what an instance graph can look like in the tool.JAVA中文站社区门户T$BJ%Zp/`

  举个例子个Hashtable可以有个来自MyServer对象引用而MyServer包含个activeSessions数据成员这些信息就足够深入代码找出问题所在 JAVA中文站社区门户{8f9{9M+W&Yf*xE;M

Figure 7. Inspecting an object and its references to other objectsJAVA中文站社区门户9j#D @S3F@3r

找出分配点  当发现了内存泄漏问题找到那些泄漏对象在何处是非常有用也许没有足够信息知道他们同其他相关对象的间联系但是有关他们在那里被创建信息还是很有帮助当然你不会愿意创建个工具来打印出所有分配堆栈路径你也不会愿意在模拟环境中运行只是为了捕捉到个内存泄漏  有了JRockit Memory Leak Detector代码可以动态在内存分配出创建堆栈路径这些堆栈路径可以在工具中累积分析如果你不启用这个工具这个特征就不会有任何消耗这就意味着时刻准备着开始当需要分配路径时JRockit编译器可以让代码不工作而监视内存分配但只对需要特定类有效更好当做完数据分析后生成机械代码会完全被移除不会引起任何执行上效率衰退

}$P7Ie\&x \Mk Figure 8. The allocation stack traces for String during execution of a sample programJAVA中文站社区门户!VxD-QFD;SC2y

整理总结  内存泄漏查找起来非常困难文章中些避免泄漏实战包括了要时刻记住把什么放进了数据结构中更接近监视内存中意外增长   我们同时也看到了JRockit Memory Leak Detector是如何捕捉产品级系统中内存泄漏该工具通过 3步思路方法发现泄漏通过趋势分析发现那些对象类存在泄漏; 2找出同泄漏对象相关其他类; 3向下发掘观察独立对象的间是如何相互联系同时该工具也可以动态找出所有内存分配堆栈路径利用这 3个特性将该工具紧紧地集成在JVM中那么就可以安全有效捕捉和修复内存泄漏了 资源  JRockit Tools Download   BEA JRockit 5.0 Documentation   New Features and Tools in JRockit 5.0   BEA JRockit DevCenter Staffan Larsen是JRockit项目工程师的这个项目是在1998年底他和别人联合创建

TAG: Java JAVA java 内存 泄漏
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: