捕获Java内存泄露 (五)

原文:Hunting Memory Leaks in Java
上一篇:捕获Java内存泄露 (四)

捕获Java内存泄露 (五)

捕获内存泄露

要找到和消除内存泄漏,您需要适当的工具。本文使用Java VisualVM来检测和排除泄漏。

用Java VisualVM远程分析堆

VisualVM是一个工具,提供了一个可视化界面查看基于Java技术的应用程序在运行时的详细的信息。

通过VisualVM,你可以查看本地应用程序和那些运行在远程主机上的应用的相关数据。你也可以捕获有关JVM软件实例的数据并保存数据到本地系统。

为了能从Java VisualVM的所有功能中受益,你应该运行Java平台标准版(Java SE)6或以上版本。

为JVM启用远程连接

在生产环境中,通常很难接触到运行我们的代码的主机。幸运的是,我们可以远程分析我们的Java应用程序。

首先,我们需要给自己授权访问目标机的JVM。要达成这个目的,先创建一个名为jstatd.all.policy的文件,写入下面内容:

grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};

一旦文件被创建,我们需要启用到目标虚拟机的远程连接,通过jstatd-虚拟机jstat守护进程工具,命令如下:

jstatd -p <PORT_NUMBER> -J-Djava.security.policy=<PATH_TO_POLICY_FILE>

举个例子:

jstatd -p 1234 -J-Djava.security.policy=D:\jstatd.all.policy

通过启动于目标VM的jstatd,我们能够连接到目标机来远程分析有内存泄漏问题的应用程序。

连接到远程主机

在客户机上,打开一个窗口然后输入jvisualvm来开启VisualVM的工具。

接下来,我们要将远程主机加入VisualVM。因为目标JVM允许从另一台装有J2SE 6或更高版本的机器来远程连接,我们启动Java VisualVM工具连接到远程主机。如果与远程主机连接成功,我们将会看到在目标JVM上运行的Java应用程序,如图所示:

RemoteHost

要配置和分析应用程序,我们只需在侧面板双击其名称。

现在我们都设置好了,让我们研究一个存在内存泄漏问题应用程序,我它起名叫MemLeak

MemLeak

当然,有许多用Java创建内存泄漏的方法。为简单起见,我们将定义一个类作为[HashMap] [5]中的key,但我们不会定义equals()和hashcode()方法。

HashMap是Map接口的hash table实现,因此,它定义的键和值的基本概念:每个值对应一个独特的键,所以如果给定键值对的键已在HashMap中,其对应值将被替换。

它有强制性的要求,即作为键的类必须提供对hashcode()equals()方法的正确的实现。没有它们,就无法保证生成一个好的键。

通过不定义equals()hashcode()方法,我们将相同键的值往HashMap里不停的放,HashMap因为无法识别这些相同的键,就不能替换的键对应的值,因而持续增长,最终抛出OutOfMemoryError

下面是MemLeak类:

package com.post.memory.leak;

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();
        }
    }
}

注意:内存泄漏不是由于第14行的无限循环造成的:无限循环会导致资源耗尽,而不是内存泄漏.。如果我们有正确的实现equals()hashcode()方法,即使有无限循环代码也会运行良好,因为我们在HashMap中只有一个元素。

(对于那些感兴趣的,这里有一些替代手段(故意)产生泄漏。)

使用Java VisualVM

通过Java VisualVM,我们可以监控Java堆和识别其行为是否在暗示一个内存泄漏。

这是MemLeak的Java堆在初始化后的图形化的展示(回忆一下我们的各代的讨论):

MemLeak_1

仅仅过了30秒,年老代几乎就满了,这表明,即使有全GC,年老代的始终是在增长的,这是内存泄漏的一个明显的信号。

一种检测泄漏的原因的方法如下图所示,它是用Java VisualVM处理heapdump生成的。在这里,我们看到,50%的Hashtable$Entry对象在堆中,而第二行向我们指出了MemLeak类。因此,这个内存泄漏是由与在MemLeak类使用HashTable引起的。

MemLeak_2

最后,年轻代和年老代都完全满了,让我们观察下这个OutOfMemoryError之后Java堆的情况。

MemLeak_3

总结

内存泄漏是最难解决的Java应用问题之一,因为症状是多种多样的,并且很难重现。在这里,我们概述了一种循序渐进的发现内存泄漏,并确定其来源的方法。但最重要的是,仔细阅读你的错误信息,并注意你的堆栈的跟踪信息,不是所有的泄漏都像它变现的那样简单。

附录

除了Java VisualVM,还有一些其他的工具,可以进行内存泄漏检测。许多泄漏检测器在库级操作,可以截获内存管理例程的调用。例如:HPROF,是一个简单的命令行工具, 它捆绑在Java 2平台标准版(J2SE),可以作堆和CPU分析。HPROF的输出可以被直接分析或作为其他工具如JHAT的输入。当我们使用Java 2企业版(J2EE)的应用时,有很多堆转储的解决方案更易于分析,如IBM内存分析器

译者注:原文成文时间比较早,主要基于Java SE 6,读者可自行搜索Java SE 7之后的变化。文中真正有价值的不是JVM的实现细节,而是对待内存泄露问题的解决思路、方向及方法论。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值