如何在您的Java应用中查找并修复内存泄漏 架构&设计

来源:互联网 / 作者:SKY / 2017-12-27 22:20 / 点击:
您是否碰到过某个Java应用程序起初运行良好,经过一段时间后却缓慢下来了?或者它在处理少量文件时性能不错,文件量一旦增加就性能下降的情况呢?如出现这样的情况

【Chinaz.com快译】您是否碰到过某个Java应用程序起初运行良好,经过一段时间后却缓慢下来了?或者它在处理少量文件时性能不错,文件量一旦增加就性能下降的情况呢?如出现这样的情况,很可能您遇到了内存泄漏的问题。

如何在您的Java应用中查找并修复内存泄漏

在应对内存泄漏时,如果有人问我:“你是否知道此事的前因后果和应对方法?”那么,我就会做出如下回答:

一、目标受众

尽管在一般情况下,本文中所介绍的方法是独立于IDE和操作系统的,但是我在此所用到的截图和说明仍然来自于Fedora Linux和带插件开发的Eclipse。

二、内存泄漏的症状

起初运行速度快,但随着时间的推移速度就慢下来了。比如说:

能够正常处理少量数据集,但应对大量数据集时出现严重的性能问题。

在您的JVM中,旧版本(Old-Generation)内存的使用率持续增加。

在您的JVM中,出现内存耗尽的跳转错误。

无故自我崩溃。

三、常见的内存泄漏

Java中的内存泄漏通常发生在您忘记关闭某个资源,或是某个对象的引用没能释放的时候。例如:

文件/文本缓冲区没被关闭。(请参见:中的案例)

在equals()和hashcode()不被使用时,各种哈希映射的引用仍然保持激活的状态,例如:

import java.util.Map; 

public class MemLeak { 

public final String key; 

public MemLeak(String key) { 

    this.key = key; 

public static void main(String args[]) { 

    try { 

      Map map = System.getProperties(); 

      for(;;) { 

         map.put(new MemLeak("key"), "value"); 

      } 

    } catch(Exception e) { 

        e.printStackTrace(); 

    } 

  } 

其他各种细节(请参见:)

那些引用了各种外部类的内部类所导致的泄漏。(可以将它们变成静态来避免,请参见:https://blogs.oracle.com/olaf/entry/memory_leaks_made_easy)。

四、如何一次性修复它们?

这里提供两种方法。第一种是尝试“快速修复”。如果此法失败,那么您就必须往下尝试一条漫长的解决之路了。

快速修复:使用Eclipse内存泄漏的警告(去捕捉一些泄漏)。

手动禁用和启用您代码的各个部分,并使用VisualVM(Jconsole或Thermostat)之类的工具观察JVM的内存使用情况。

1. 快速修复:Eclipse内存泄漏的警告/错误。

为了遵从JDK 1.5+的代码规范,Eclipse会向您“抛出”一些明显泄漏用例的警告和错误。更精确地说,任何使用了closable(如1.5后出现的outputStream)的对象,如果它的引用是被销毁而不是封闭的话,就会抛出一个警告。然而在Eclipse的各个项目中,其检漏功能并非总是被启用的。因此,为了事先打开它们,您可以到项目的设置里,按照下图所示进行开启:

快速修复:Eclipse内存泄漏的警告/错误

此处Eclipse罗列出了各种内存泄漏:

Eclipse罗列出了各种内存泄漏

然而,就算使用了Eclipse的此项功能,系统仍无法探测到所有的文件关闭与泄漏。尤其是在使用旧式(1.5之前)代码时,您很可能会因为它们在使用过程中仅仅只是“关闭”(closable)了,而遇到泄漏问题了。也有时候,文件在深度嵌套中被打开/关闭,也会导致Eclipse无法检测到。因此如果您碰到这种情况,就可能需要去尝试第2种方法了。

2. 手动禁用和启用您代码的各个部分,并使用VisualVM之类的工具观察JVM的内存使用情况。

如果您步入了这一步,那就不得不卷起袖子,做一些体力劳动了。您需要通读您的所有代码,以试图找出发生泄漏的地方。作为帮助,我建议您使用VisualVM之类的工具(当然,Thermostat和MAT也是可行的)。

a. 配置的VisualVM

(1) 下载该工具。

(2) 打开终端,到达目录.../visualvm_xyz/bin下,运行shell脚本'./visualvm' (或在Windows上运行visualvm.exe)。

(3) 您会看到弹出的主窗口。如果展开“本地”并双击您正在运行的应用(如下图,我的应用是一个子Eclipse),您就可以看到它的各种属性。

如何在您的Java应用中查找并修复内存泄漏

(4) 在Fedora上用VisualVM进行故障诊断:对我来说,最初我无法连接到自己的JVM,也不能够使堆转储(heap-dumps)和分析(profiling)运行起来。于是我探索出了如下步骤:

确保用自己的登录用户身份运行它,而不是使用sudo。

对系统进行全面更新(sudo yum update)。

考虑重新启动是否有所帮助。

尝试在关闭所有正在运行的Java应用程序之后,再启动VisualVM。

(5) 添加一些插件。在使用VisualVM之前,我事先添加了一些插件。请点击进入工具->插件->“可用插件”。请选择如下的插件(如果您喜欢,则可以随意浏览并添加更多的插件):

内存池

可视的GC

终止应用程序

b. 用VisualVM分析运行的代码

(1) 现在运行您的Java应用程序。

(2) 将VisualVM连接到您的应用程序。

(3) 执行那些容易导致性能变缓的操作。

(4) 检查“监控”和“内存池”选项卡。如果您看到在“监视器”选项卡中内存显示增加的话,那就按下“执行GC”(垃圾收集),并监视内存的使用情况是否有所减少。

如何在您的Java应用中查找并修复内存泄漏

阅读延展

1
3