摘 要 稳定性是衡量软件系统质量的重要指标,内存泄漏是破坏系统稳定性的重要因素。由于采用垃圾回收机制,Java语言的内存泄漏的模式与C++等语言相比有很大的不同。全文通过与C++中的内存泄漏问题进行对比,讲述了Java内存泄漏的基本原理,以及如何借助Optimizeit profiler工具来测试内存泄漏和分析内存泄漏的原因,在实践中证明这是一套行之有效的方法。
关键词 Java; 内存泄漏; GC(垃圾收集器) 引用; Optimizeit
问题的提出
笔者曾经参与开发的网管系统,系统规模庞大,涉及上百万行代码。系统主要采用Java语言开发,大体上分为客户端、服务器和数据库三个层次。在版本进入测试和试用的过程中,现场人员和测试部人员纷纷反映:系统的稳定性比较差,经常会出现服务器端运行一昼夜就死机的现象,客户端跑死的现象也比较频繁地发生。对于网管系统来讲,经常性的服务器死机是个比较严重的问题,因为频繁的死机不仅可能导致前后台数据不一致,发生错误,更会引起用户的不满,降低客户的信任度。因此,服务器端的稳定性问题必须尽快解决。
解决思路
通过察看服务器端日志,发现死机前服务器端频繁抛出OutOfMemoryException内存溢出错误,因此初步把死机的原因定位为内存泄漏引起内存不足,进而引起内存溢出错误。如何查找引起内存泄漏的原因呢?有两种思路:第一种,安排有经验的编程人员对代码进行走查和分析,找出内存泄漏发生的位置;第二种,使用专门的内存泄漏测试工具Optimizeit进行测试。这两种方法都是解决系统稳定性问题的有效手段,使用内存测试工具对于已经暴露出来的内存泄漏问题的定位和解决非常有效;但是软件测试的理论也告诉我们,系统中永远存在一些没有暴露出来的问题,而且,系统的稳定性问题也不仅仅只是内存泄漏的问题,代码走查是提高系统的整体代码质量乃至解决潜在问题的有效手段。基于这样的考虑,我们的内存稳定性工作决定采用代码走查结合测试工具的使用,双管齐下,争取比较彻底地解决系统的稳定性问题。
在代码走查的工作中,安排了对系统业务和开发语言工具比较熟悉的开发人员对应用的代码进行了交叉走查,找出代码中存在的数据库连接声明和结果集未关闭、代码冗余和低效等故障若干,取得了良好的效果,文中主要讲述结合工具的使用对已经出现的内存泄漏问题的定位方法。
内存泄漏的基本原理
在C++语言程序中,使用new操作符创建的对象,在使用完毕后应该通过delete操作符显示地释放,否则,这些对象将占用堆空间,永远没有办法得到回收,从而引起内存空间的泄漏。如下的简单代码就可以引起内存的泄漏:
void function(){
Int[] vec = new int[5];
}
在function()方法执行完毕后,vec数组已经是不可达对象,在C++语言中,这样的对象永远也得不到释放,称这种现象为内存泄漏。
而Java是通过垃圾收集器(Garbage Collection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间。在下面的代码中,循环申请Object对象,并将所申请的对象放入一个Vector中,如果仅仅释放对象本身,但是因为Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。
Vector v = new Vector(10);
for (int i = 1; i < 100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。
实际上无用,而还被引用的对象,GC就无能为力了(事实上GC认为它还有用),这一点是导致内存泄漏最重要的原因。
Java的内存回收机制可以形象地理解为在堆空间中引入了重力场,已经加载的类的静态变量和处于活动线程的堆栈空间的变量是这个空间的牵引对象。这里牵引对象是指按照Java语言规范,即便没有其它对象保持对它的引用也不能够被回收的对象,即Java内存空间中的本原对象。当然类可能被去加载,活动线程的堆栈也是不断变化的,牵引对象的集合也是不断变化的。对于堆空间中的任何一个对象,如果存在一条或者多条从某个或者某几个牵引对象到该对象的引用链,则就是可达对象,可以形象地理解为从牵引对象伸出的引用链将其拉住,避免掉到回收池中;而其它的不可达对象由于不存在牵引对象的拉力,在重力的作用下将掉入回收池。在图1中,A、B、C、D、E、F六个对象都被牵引对象所直接或者间接地“牵引”,使得它们避免在重力的作用下掉入回收池。如果TR1-A链和TR2-D链断开,则A、B、C三个对象由于失去牵引,在重力的作用下掉入回收池(被回收),D对象也是同样的原因掉入回收池,而F对象仍然存在一个牵引链(TR3-E-F),所以不会被回收,如图2、3所示。
图1 初始状态
图2 TR1-A链和TR2-D链断开,A、B、C、D掉入回收池
图3 A、B、C、D四个对象被回收
通过前面的介绍可以看到,由于采用了垃圾回收机制,任何不可达对象都可以由垃圾收集线程回收。因此通常说的Java内存泄漏其实是指无意识的、非故意的对象引用,或者无意识的对象保持。无意识的对象引用是指代码的开发人员本来已经对对象使用完毕,却因为编码的错误而意外地保存了对该对象的引用(这个引用的存在并不是编码人员的主观意愿),从而使得该对象一直无法被垃圾回收器回收掉,这种本来以为可以释放掉的却最终未能被释放的空间可以认为是被“泄漏了”。
这里通过一个例子来演示Java的内存泄漏。假设有一个日志类Logger,其提供一个静态的log(String msg)方法,任何其它类都可以调用Logger.Log(message)来将message的内容记录到系统的日志文件中。Logger类有一个类型为HashMap的静态变量temp,每次在执行log(message)方法的时候,都首先将message的值丢入temp中(以当前线程+当前时间为键),在方法退出之前再从temp中将以当前线程和当前时间为键的条目删除。注意,这里当前时间是不断变化的,所以log方法在退出之前执行删除条目的操作并不能删除方法执行之初丢入的条目。这样,任何一个作为参数传给log方法的字符串最终由于被Logger的静态变量temp引用,而无法得到回收,这种违背实现者主观意图的无意识的对象保持就是我们所说的Java内存泄漏。
http://dev.yesky.com/56/2591556.shtml
分享到:
相关推荐
Java系统中的内存泄漏是一个复杂且严重的问题,尤其是在大型软件系统中,它会直接影响系统的稳定性和性能。内存泄漏不同于C++等语言,Java由于其垃圾回收(Garbage Collection, GC)机制,内存泄漏的表现形式有所...
在Android系统中,内存泄漏是一个严重的问题,它会导致应用程序占用过多的内存,进而影响设备性能,甚至可能导致应用崩溃。理解并有效地分析内存泄漏是每个Android开发者必须掌握的关键技能。 内存泄漏通常发生在...
因此,了解Java内存泄漏的成因、检测方法以及解决方案对于保证应用的高效稳定运行至关重要。 #### 2. Java内存回收机制 Java的内存管理主要集中在堆(Heap)区域,其中对象的创建通常是通过`new`关键字或反射方式...
Java内存泄漏是一个严重的问题,它会导致程序性能下降,甚至可能导致应用程序崩溃。为了有效地诊断和解决这类问题,开发者需要借助特定的分析工具。本篇将详细探讨Java内存泄漏及其相关的分析工具。 内存泄漏是指...
在内存泄露测试中,测试用例应尽可能覆盖所有可能引起内存泄露的情况,包括但不限于: - 大量创建和销毁对象。 - 异常处理不当导致的资源未释放。 - 循环引用导致的对象无法被垃圾回收机制回收。 ##### 5.2 测试...
在Java编程环境中,调用外部动态链接库(DLL)是一个常见的需求,特别是在需要与...在实际项目中,对本地代码进行充分的测试和调试,以及使用内存分析工具监控Java进程的内存使用情况,都是确保代码健壮性的重要环节。
Java内存泄露检测是Java开发中一个关键的议题,因为它直接影响到程序的稳定性和资源效率。内存泄露是指程序中已分配的内存无法被正确地释放,从而导致系统资源的浪费和可能导致程序性能下降甚至崩溃。 首先,理解...
通过设置环境变量、调用mtrace函数以及编写适当的测试代码,可以轻松地识别出程序中的内存泄露。需要注意的是,mtrace只能检测通过malloc/free机制分配和释放的内存,对于其他类型的内存错误(比如越界访问),还...
### 性能测试之内存泄露篇 #### 一、概念 在进行性能测试时,内存泄露是一个非常重要的问题。本文将详细介绍内存泄露及其与内存溢出的区别,并介绍如何监测和解决这些问题。 **内存泄露**指的是应用程序在运行...
Java内存泄露问题是软件开发过程中常见的难题之一。通过对内存管理机制的理解及上述几种常见内存泄露场景的认识,开发者可以更好地预防和解决这类问题。为了进一步提高代码的质量,建议在开发过程中结合单元测试和...
检测Java内存泄漏的方法包括使用JVM提供的工具,如VisualVM、JProfiler等,它们可以监测堆内存使用情况,定位对象生命周期,找出长时间存活但不再使用的对象。此外,还可以通过代码审查、单元测试、压力测试等手段...
在Java开发中,通过单元测试可以有效地检测内存泄露等问题,避免在生产环境中出现严重问题。 **单元测试与内存泄露的关系:** 1. **早期发现问题**:通过编写针对特定功能模块的单元测试,可以在开发早期阶段发现...
5. **LeakCanary**:对于Android应用开发者,LeakCanary是一款自动化内存泄漏检测工具,可以在应用运行时自动检测内存泄露,并提供详细的泄漏堆栈追踪。 在检测到内存泄露后,解决方法通常涉及以下几个步骤: 1. *...
- **ProGuard**:一个代码混淆和优化工具,也可以用来检查内存泄漏,尤其是在代码混淆后。 #### 二、MAT的安装与使用 1. **安装MAT**: - 打开Eclipse,选择`Help -> Install New Software...` - 在`Work with:`...
中Java应用内存泄漏的检测,通过监控集合类对象的内存消耗和集合内元素的 使用情况,得出对象内存泄漏的可能性大小,量化对象内存泄漏的风险。检测 系统首先收集垃圾回收事件后的应用内存数据,确定进行...
解决Java内存溢出和内存泄露的方法主要包括以下几点: 1. 适当调整JVM参数:通过设置-Xms和-Xmx指定堆内存的初始大小和最大大小,避免因动态扩展导致的溢出。同时,可以通过-Xss设置线程栈的大小,防止栈溢出。 2....