`

Java程序性能优化之找出内存溢出元凶

阅读更多
我曾经在刚入行的时候做过一个小的swing程序,用到了java SE,swing,Thread等东东,当初经验少也没有做过严格的性能测试,布到生产环境用了一段时间后发现那个小程序有时候会抛java.lang.OutofMemoryError异常,就是java的内存溢出。当时也上网查了不少资料,试过一些办法,代码也稍微做了些优化,但是有一个问题我始终是找不到解决的方案 - 不知为什么子窗体关闭后java的垃圾回收机制无法回收其资源,因为这个Java程序可能要经常开关一些子窗体,那么这些子窗体关闭后无法释放资源就造成了Java程序OutOfMemoryError的潜在的隐患!

最近无意间在网上看到了一个监控java程序内存使用的工具 - JProbe,马上回想起那个有关内存溢出的难题,于是我就下载了JProbe8.0.0希望从分析内存入手找到我要的答案。软件下载安装后,在安装目录里详尽的用户指南(懂点软件和英语的人很快就能上手),主要是两个步骤:

1.用JProbe Config工具根据提示生成J2SE或者J2EE程序的控制脚本(一个.jpl文件和一个.bat文件),在命令行里进入.bat文件所在的目录,然后执行该批处理让要监控的java程序跑起来

2.运行JProbe Console工具,点击“Attach to Session...”按钮,弹出java程序的内存实时监控图表“Runtime Summary”,我们主要是看“Data”卡片里的内容(注意:第一次使用该软件可能会遇到一些小问题:比如发布为jar包的程序如果运行时会去读配置文件,从控制脚本启动的话,可能会发生配置文件找不到的异常,解决办法是:不要打jar包,直接就用文件夹发布;还有可能因为一些杀毒软件的网络防火墙导致JProbe无法连接到控制脚本的session,造成监控图表打不开,解决办法是:取消防火墙对于JProbe访问网络的限制)

实时监控图表“Runtime Summary”如下图所示:


[img]
http://images.51cto.com/files/uploadimg/20090615/1001490.gif
[/img]
可以设置要监视的包或者类,然后点击“Refresh Runtime Data”按钮刷新这些对象占用内存的情况,当你觉得某个类比较可疑的话,你可以在不断的使用程序的过程中监视它的生命周期,看看它是否像预期的那样在结束了生命周期后占用的内存就被释放。众所周知:java的内存回收是自动进行的,无需程序员干预,我们称其为垃圾回收,这个垃圾回收可能是不定期的,就是当程序占用内存资源比较少的情况下可能jvm的垃圾回收频率就比较低;反之,java程序消耗内存资源比较多的情况下,垃圾回收的频率和力度就比较高,这种垃圾回收的不确定性很可能会影响我们的判断,但我们可以点击JProbe监控界面右上方的“Request a Garbage Collection”(像一个垃圾桶的图标)按钮来向jvm发出垃圾回收的请求,等几秒后再去点击“Refresh Runtime Data”,这个时候如果那个预期应该已经销毁的对象的类名还是没有从监控界面下方的class列中消失或者其对象数量没有减少的话(请多试几次,中间可以夹杂些其他增加程序内存使用的操作以确保jvm确实执行了垃圾回收),那恭喜你!90%的可能性你已经找到了程序的某个缺陷

这个查找元凶的过程可能是相当耗时的,是对程序员的耐心的挑战。我熬了一下午一晚上,功夫不负有心人,基本上把我那个小程序的所有内存溢出的漏洞都找到并补上了。事实告诉我之前那个子窗体关闭后资源无法释放的根本原因是:子窗体虽然调用了dispose()方法,但是子窗体对象的引用一直都在:或者是被静态HashMap引用、或者是它的内部子线程类没有释放、或者是它的某个事件监听类没有释放(借用JProbe的火眼金睛一检验,发现问题真是一大堆啊!),so.我们要彻底释放某个对象占用资源的关键在于找到并释放所有对它的引用!

下面是我解决具体问题的一些经验:

程序中造成内存溢出可能性最大的是HashMap,Hashtable等等集合类,尤其是静态的,更是要慎之又慎!!!它们引用的对象可能你感觉已经销毁了,其实很可能你忘记remove键值,而如果这些集合对象还是静态的挂在其他类里面,那么这个引用可能一直都在,借用JProbe测试一下,结果往往出人意料,解决办法:彻底删除键,remove、clear,如果允许最好把集合对象设为null

对于不再使用的线程对象,如果要彻底杀了它,很多书上都推荐用join方法,我之前也是这样做的,但后来借助JProbe工具我吃惊的发现这样做很可能要杀的线程仍旧好好的活在你日益增大的内存里,很可能调用了线程的sleep方法后用join方法就会有点问题,解决办法:在join方法前再加一句执行interrupt方法,不过这个时候可能会有新的问题:执行interrupt方法后你的线程类会抛InterruptedException,上有政策下有对策,加一个开关变量做判断就能完美解决,可参考下面的代码:

Java代码:

/**   
* <p>Description: 创建线程的内部类</p>   
* @author cuishen   
* @version 1.1   
*/   
class NewThread implements Runnable {    
    Thread t;    
    NewThread() {    
        t = new Thread(this, path);    
        t.start();    
    }    
    public void run() {    
        try {    
            while(isThreadAlive) {    
                startMonitor();    
                Thread.sleep(Long.parseLong(controlList.get(controlList.size() - 1).toString()));    
            }    
        } catch (InterruptedException e) {    
            if(!ifForceInterruptThread) {//开关变量    
                stopThread(logThread);    
                String error = "InterruptedException!!!" + path + ": Interrupted,线程异常终止!程序已试图重启该线程!!";    
                System.err.println(error);    
                LogService.writeLog(error);    
                createLogThread();    
            }    
        }    
    }    
}    
   
public void createLogThread() {    
    ifForceInterruptThread = false;//开关变量    
    logThread = new NewThread();    
}    
   
private void stopThread(NewThread thread) {    
    try {    
        thread.t.join(100);    
    } catch (InterruptedException ex) {    
        System.out.println("线程终止异常!!!");    
    } finally {    
        thread = null;    
    }    
}    
   
/**   
* 关闭并彻底释放该线程资源的方法   
*/   
public void stopThread() {    
    try {    
        ifForceInterruptThread = true;//开关变量    
        isThreadAlive = false;    
        logThread.t.interrupt();    
        logThread.t.join(50);    
    } catch (InterruptedException ex) {    
        System.out.println("线程终止异常!!!");    
    } finally {    
        this.controlList = null;    
        this.keyList = null;    
        logThread = null;    
    }    
}  

对于继承JFrame的窗体类,我们要注意在初始化方法中加入:this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); ,并且注意和其关联的事件监听类一律写成窗体类的内部类,这样窗体dispose()的时候,这些内部类也一并销毁,就不会再有什么莫名其妙的引用了。
分享到:
评论

相关推荐

    JAVA内存溢出问题总结

    JAVA内存溢出问题总结 JAVA 内存溢出问题是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行...内存溢出问题是 Java 开发中常见的错误,通过调整容器参数和优化程序代码,可以避免内存溢出的发生。

    java IBM websphere 内存溢出 javacore deapdump CPU内存分析工具

    Java IBM WebSphere应用服务器在运行过程中可能会遇到各种性能问题,其中最常见的挑战之一是内存溢出。内存溢出是指应用程序消耗的内存超过了系统所能提供的限制,导致程序崩溃或性能急剧下降。在这种情况下,开发者...

    完美解决java读取excel内存溢出问题.rar

    总结来说,解决Java读取Excel内存溢出问题,关键在于合理利用资源、优化代码逻辑以及选择适合的API,如Apache POI的SXSSF。通过这些方法,我们可以在不显著增加系统资源负担的情况下,高效地处理大Excel文件。

    Java内存溢出解决办法

    理解并解决Java内存溢出问题对于优化应用性能、提高系统稳定性至关重要。 首先,我们需要了解Java内存模型。Java虚拟机(JVM)将内存分为几个区域:堆(Heap)、栈(Stack)、方法区(Method Area)、程序计数器...

    内存溢出配置,内存溢出配置

    内存溢出配置是IT行业中,尤其是在Java应用开发与运维领域中的一个重要话题,它涉及到系统资源管理、性能调优以及故障排查等多个方面。标题与描述中重复提到“内存溢出配置”,这表明了对这一主题的关注与重视。接...

    内存泄漏与内存溢出

    在软件开发领域,尤其是涉及到数据库应用的场景下,内存泄漏(Memory Leak)和内存溢出(Out of Memory,简称OOM)是两个常见的问题,它们直接影响到程序的性能、稳定性和可扩展性。以下是对这两个概念及其相关知识...

    java解决大批量数据导出Excel产生内存溢出的方案

    - 使用内存分析工具(如VisualVM或YourKit Java Profiler)监控内存使用情况,找出内存泄漏或不必要的对象引用,进行优化。 通过这些方法,我们可以在处理大量数据导出到Excel时避免内存溢出问题,同时保持程序的...

    kettle内存溢出(Java heap space)以及解决方法.docx

    当程序创建新的对象并分配给堆时,如果堆空间不足,就会抛出`OutOfMemoryError: Java heap space`异常。Java应用程序可以通过设置JVM的启动参数来控制堆的大小,例如`-Xms`和`-Xmx`分别用于设置初始堆大小和最大堆...

    Java 内存溢出解决办法

    Java内存溢出问题通常指的是程序在试图分配内存时,无法找到足够的连续内存空间而抛出的异常。在Java中,内存分为几个区域:堆(Heap)、栈(Stack)、方法区(Method Area,Java 8后被元空间取代)以及程序计数器和...

    java内存泄露、溢出检查方法和工具

    Java内存管理是开发Java应用程序时的关键环节,内存泄露和溢出问题可能导致系统性能下降,甚至导致服务崩溃。本文将深入探讨如何检测和分析Java内存泄露与溢出,并介绍一种常用的工具——Memory Analyzer(MAT)。 ...

    JAVA内存溢出详解.doc

    Java内存溢出(Out Of Memory,OOM)是Java应用程序运行时常见的问题,它通常发生在程序对内存需求超过了Java虚拟机(JVM)所能提供的可用内存时。本文将深入探讨Java内存溢出的原因、表现以及如何解决。 1. **Java...

    JAVA内存溢出

    JAVA内存溢出 JAVA中OutOfMemoryError(内存溢出)的三种情况及解决办法 Java中的OutOfMemoryError(内存溢出)是一种常见的错误,本文将详细介绍OutOfMemoryError的三种情况及其解决方法。 首先,我们需要了解...

    java 内存溢出分析工具 HeapAnalyzer

    Java内存溢出(Out of Memory,OOM)是Java应用程序中常见的问题,会导致程序崩溃或性能急剧下降。HeapAnalyzer是一款强大的工具,专为分析Java应用程序的内存状况,特别是针对内存溢出问题进行诊断。本文将详细介绍...

    解决Java程序内存溢出的办法

    在Java编程中,内存溢出(Out...总之,解决Java程序内存溢出问题需要深入理解内存管理、JVM参数、垃圾回收以及代码优化等多个方面。通过不断学习和实践,开发者可以有效地预防和解决这类问题,确保程序的稳定性和性能。

    java内存溢出解决方案

    Java内存溢出问题通常指的是Java应用程序在运行过程中由于内存分配不当或使用过度导致JVM无法分配更多的内存,从而抛出`java.lang.OutOfMemoryError`异常。本文将深入探讨Java内存溢出的不同类型及其解决方案。 1. ...

    从 Java 代码到 Java 堆 理解和优化您的应用程序的内存使用

    【描述】:本文旨在帮助Java开发者深入了解从编写代码到Java堆的内存管理过程,以便更好地优化应用程序的内存使用。通过分析Java代码中的内存开销,以及讨论对象创建、集合效率等方面,将揭示如何确定性能瓶颈并改进...

    内存溢出解决

    在Java开发中,内存管理是确保应用程序稳定运行的关键因素之一。当程序运行时分配给它的内存空间不足以支撑其正常运行时,就会出现“内存溢出”(Out of Memory Error,简称OOM)。这通常发生在堆内存或方法区等内存...

    android图片瀑布流优化版,防止内存溢出

    综上所述,这个优化版的Android图片瀑布流解决方案综合运用了多种技术手段,以防止内存溢出,提升应用的稳定性和性能。开发者在实现自己的瀑布流布局时,可以参考这些优化策略,为用户提供流畅且高效的图片浏览体验...

    was内存溢出 javacore分析工具jca 456

    在IT行业中,尤其是在Java应用程序的运行环境中,内存溢出(Memory Leak)是一个常见的问题,尤其在大型企业级应用服务器如WebSphere中。本篇将详细探讨如何利用Javacore分析工具JCA 456来诊断和解决这类问题。 ...

Global site tag (gtag.js) - Google Analytics