JVM内存泄漏问题产生及检测方案

开发 后端
随着越来越多的服务器程序采用Java技术,在很多嵌入式系统中,内存的总量非常有限,JVM内存泄露问题也就变得十分关键,即使每次运行少量泄漏,长期运行之后,系统也是面临崩溃的危险。

这里向大家描述一下JVM内存泄露的概念和检测方法,Java的一个重要优点就是通过垃圾收集器(GarbageCollection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java不存在JVM内存泄漏问题,或者认为即使有JVM内存泄漏也不是程序的责任,而是GC或JVM的问题。其实,这种想法是不正确的,因为Java也存在内存泄露,但它的表现与C++不同。

JVM内存泄漏问题的提出

Java的一个重要优点就是通过垃圾收集器(GarbageCollection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java不存在JVM内存泄漏问题,或者认为即使有JVM内存泄漏也不是程序的责任,而是GC或JVM的问题。其实,这种想法是不正确的,因为Java也存在内存泄露,但它的表现与C++不同。

随着越来越多的服务器程序采用Java技术,例如JSP,Servlet,EJB等,服务器程序往往长期运行。另外,在很多嵌入式系统中,内存的总量非常有限。JVM内存泄露问题也就变得十分关键,即使每次运行少量泄漏,长期运行之后,系统也是面临崩溃的危险。

什么是Java中的内存泄露

下面,我们就可以描述什么是JVM内存泄漏。在Java中,JVM内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的JVM内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

在C++中,JVM内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。

通过分析,我们得知,对于C++,程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java提高了编程的效率。

JVM内存泄露与C++的比较

因此,通过以上分析,我们知道在Java中也有JVM内存泄漏,但范围比C++要小一些。因为Java从语言上保证,任何对象都是可达的,所有的不可达对象都由GC管理。

对于程序员来说,GC基本是透明的,不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpotJVM就支持这一特性。

下面给出了一个简单的内存泄露的例子。在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个Vector中,如果我们仅仅释放引用本身,那么Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。

  1. Vectorv=newVector(10);  
  2. for(inti=1;i<100;i++)  
  3. {  
  4.  Objecto=newObject();  
  5.  v.add(o);  
  6.  o=null;   

//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。#p#

如何检测JVM内存泄漏

***一个重要的问题,就是如何检测Java的JVM内存泄漏。目前,我们通常使用一些工具来检查Java程序的JVM内存泄漏问题。市场上已有几种专业检查JavaJVM内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java程序运行时,所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有JVM内存泄漏问题。这些工具包括OptimizeitProfiler,JProbeProfiler,JinSight,Rational公司的Purify等。

Optimizeit基本功能和工作原理

OptimizeitProfiler版本4.11支持Application,Applet,Servlet和RomoteApplication四类应用,并且可以支持大多数类型的JVM,包括SUNJDK系列,IBM的JDK系列,和Jbuilder的JVM等。并且,该软件是由Java编写,因此它支持多种操作系统。Optimizeit系列还包括ThreadDebugger和CodeCoverage两个工具,分别用于监测运行时的线程状态和代码覆盖面。

当设置好所有的参数了,我们就可以在OptimizeIt环境下运行被测程序,在程序运行过程中,Optimizeit可以监视内存的使用曲线(如下图),包括JVM申请的堆(heap)的大小,和实际使用的内存大小。另外,在运行过程中,我们可以随时暂停程序的运行,甚至强行调用GC,让GC进行内存回收。通过内存使用曲线,我们可以整体了解程序使用内存的情况。这种监测对于长期运行的应用程序非常有必要,也很容易发现JVM内存泄露。

检测JVM内存泄露


在运行过程中,我们还可以从不同视角观查内存的使用情况,Optimizeit提供了四种方式:

◆堆视角。这是一个全面的视角,我们可以了解堆中的所有的对象信息(数量和种类),并进行统计、排序,过滤。了解相关对象的变化情况。

◆方法视角。通过方法视角,我们可以得知每一种类的对象,都分配在哪些方法中,以及它们的数量。

◆对象视角。给定一个对象,通过对象视角,我们可以显示它的所有出引用和入引用对象,我们可以了解这个对象的所有引用关系。

◆引用图。给定一个根,通过引用图,我们可以显示从该顶点出发的所有出引用。

在运行过程中,我们可以随时观察内存的使用情况,通过这种方式,我们可以很快找到那些长期不被释放,并且不再使用的对象。我们通过检查这些对象的生存周期,确认其是否为内存泄露。在实践当中,寻找内存泄露是一件非常麻烦的事情,它需要程序员对整个程序的代码比较清楚,并且需要丰富的调试经验,但是这个过程对于很多关键的Java程序都是十分重要的。

JVM内存泄漏总结

综上所述,Java也存在内存泄露问题,其原因主要是一些对象虽然不再被使用,但它们仍然被引用。为了解决这些问题,我们可以通过软件工具来检查内存泄露,检查的主要原理就是暴露出所有堆中的对象,让程序员寻找那些无用但仍被引用的对象。
 

【编辑推荐】

  1. Java内存泄漏及检测方法
  2. 探究JVM1.6与JVM1.5性能差距
  3. JVM监控在本地和远程的应用
  4. 全面认识Eclipse中JVM内存设置
  5. JVM启动参数中标准参数列表速查手册

 

 

责任编辑:佚名 来源: ibm.com
相关推荐

2018-12-07 10:52:08

内存泄漏方法

2010-09-25 11:07:45

Java内存泄漏

2024-07-03 11:28:15

2015-07-10 09:15:47

LeakCanary内存泄漏

2023-10-31 16:40:38

LeakCanary内存泄漏

2016-08-22 08:36:14

ReactiveCoc内存泄漏GitHub

2022-09-09 15:58:29

HiveServerHive 组件Java 开发

2016-12-15 21:47:11

Android内存泄漏

2010-09-27 13:14:42

JVM内存限制

2011-06-16 09:28:02

C++内存泄漏

2009-06-16 11:20:22

内存泄漏

2012-01-11 10:45:57

JavaJVM

2024-02-21 08:00:55

WindowsDWM进程

2018-10-25 15:24:10

ThreadLocal内存泄漏Java

2017-01-05 19:34:06

漏洞nodejs代码

2024-08-05 10:40:58

2024-01-30 10:12:00

Java内存泄漏

2010-09-27 13:41:22

JVM内存回收

2010-09-26 16:04:48

JVM内存溢出

2022-09-28 10:35:31

JavaScript代码内存泄漏
点赞
收藏

51CTO技术栈公众号