浅谈并小结java内存泄漏

一.定义

首先什么是内存泄漏,简单点说就是用完了忘了回收,而其他对象等资源想用却没法用的一种“站着茅坑不拉屎”的浪费资源的情况。在C/C++中,多数泄漏的场景就是程序离开某一运行域时,如在某个方法体中new出的对象或者malloc出的结构体等,并且只有该方法体中的局部变量指向这块内存区域时,在该方法返回时,存在栈中的局部变量指针随着栈帧被一起销毁,那么就没有任何指针可以指向该内存区域了,那么这块内存区域便是泄漏了。

而java的内存泄漏呢?众所周知,java的内存回收是由gc管理的。gc运行的是可达性算法,jvm中的gc线程将java对象从一个特定的对象gcroot开始,看成一个树,遍历树以判断对象的可达性,和C++那种静态的指针计数拥有本质区别,不存在无用对象间相互引用造成的困扰。也就是说当java对象真正确实没有任何一个(强)引用(弱引用等除外)的时候,java对象才可能会被回收。也就是说,这里的java内存泄漏和传统的C/C++是不尽相同的

下图是java的可达性算法:

二.什么是java的内存泄漏

个人理解的java内存泄漏,其实可以描述为:对象遗忘或者说是找不到引用。因为在java中如果对象还在heap中存活的话(死亡对象gc回收之前除外),那么必然在整个环境中有引用指向这个对象,简而言之,就是“此人还活着必有其存在的意义”。

那么问题来了,那个引用在哪呢?我们开发者根本在代码里看不见啊?别急,这个就是java内存泄漏的关键所在。很多时候这些看不见的引用藏在了某框架中,某静态变量中,或者某还在运行的子线程中。。。。

下面举几个例子说不定你就明白了。

三.常见场景

1.HashMap,有时候,我们一不小心把可变hashcode的对象作为HashMap的key,当你一不小心改变了对象的hashcode的时候,key就无法取出对应的value,此value对于你便不可达了。但是value的引用还是存在与HashMap框架内部的数组和链表结构中。而你又无可奈何,内存便”泄漏“了。

2.Android中常见的Context泄漏(Activity代表)。在Android中,给Context对象引用要小心处理,究其根源是因为Android应用层框架帮我们管理了Activity(Context)对象的生命周期,这类差不多可以代表我们现在常用的IOC框架,框架管理对象的生灭,开发者只有应用权。如果你不小心将Context对象塞进了哪个静态变量中,或者引用到了哪个长周期的子线程中等,在Activity(Context)结束时,框架层跑完所有销毁程序,移除了它对Context的引用,这时框架层就认为这个Context已经死亡了。而框架却没有想到在他外部还有其他引用没有释放。这样就造成了泄漏。

3.静态变量,这个不用过多阐述,很好理解。

4.资源对象用完没有释放,如数据库,流这些资源类,如Cusor,File.Socket等。原因是因为这些与本地平台交互的接口涉及JNI,可能有native层的指针引用了java层的对象,没有释放的话,引用是一直存在的。

5.非静态的内部类,尤见于Activity内部的Handler和Runnable等,和上一个阐述的关联起来。非静态内部类包含一个对外部类的强引用,而且这个引用是编译器自己加的,开发者在源码中看不见,故比较隐蔽。

如果你反编译一个在Activity中的内部类你会发现:

# instance fields
.field final synthetic this$0:Lcom/gy/just/VoltageMonitor/View/Activity/YunWeiActivity;

.field final synthetic val$sec:I

# direct methods
.method constructor <init>(Lcom/gy/just/VoltageMonitor/View/Activity/YunWeiActivity;I)V
    .locals 0
    .param p1, "this$0"    # Lcom/gy/just/VoltageMonitor/View/Activity/YunWeiActivity;

编译器自动给内部类加了一个叫“this$0”的外部类变量,并且在构造函数中赋值。那么内部类中就持有有了一个不可见的外部类强引用。像Runnable,Handler这类的内部类如果子线程没有结束的话,那么外部类对象的泄漏就容易发生了。

那么,我们来做一个有趣的小实验,我们如果把内部类中外部类的引用置空的话,会发生什么有趣的现象?

public class B extends A{

	public void test(){
		new Thread(new Demo()).start();
	}

	@Override
	public void dosth() {
		// TODO Auto-generated method stub
		System.out.println("doB");
	}

class Demo implements Runnable{

		public Demo(){
			try {
				Field field = getClass().getDeclaredField("this$0");
				field.setAccessible(true);
				field.set(this, null);
			} catch (NoSuchFieldException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub

			dosth();

		}
	}
}

运行异常如下

public abstract interface java.util.List<E>
Exception in thread "Thread-0" java.lang.NullPointerException
	at test.B$Demo.run(B.java:46)
	at java.lang.Thread.run(Thread.java:745)

怎么样?内部类Demo已经无法调用外部类的dosth方法了,抛出空指针异常。

四.总结,严格意义上说java是没有真正的内存泄漏的,这些泄漏和我们看不见的框架内部息息相关,所以养成良好的编程习惯,理解理解常用框架的结构原理,就能避免这些错误。最后,如果大家觉得小弟理解的还不错的,不妨给个赞,道友们觉得理解上有错误的也欢迎指正,毕竟这是我的个人看法。

时间: 2024-07-29 12:53:20

浅谈并小结java内存泄漏的相关文章

Java内存泄漏分析与解决方案

Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统崩盘,作者用自已的亲身经历与各位网友分享解决这些问题的办法. 作为Internet最流行的编程语言之一,Java现正非常流行.我们的网络应用程序就主要采用Java语言开发,大体上分为客户端.服务器和数据库三个层次.在进入测试过程中,我们发现有一个程序模块系统内存和CPU资源消耗急剧增加,持续增长到出现java.lang.Ou

浅谈C++容器动态内存管理的优化

在信息学竞赛中,C++的容器的用途非常广泛,但经常因常数过大而超时.怎样才能提高它们的效率呢? 我们知道,容器是存储同一类对象的对象,既然"对象"我们无法改变,那么我们只能从"存储"入手,不难想到,不同容器在实现上的根本区别是它们对应着不同的内存组织方式,内存管理无疑是这种实现的核心,所以优化内存管理是加快容器效率的最好途径之一. 一.内存分配器简介 怎样才能优化内存管理呢?很简单,C++为我们提供了这样的接口,我们可以通过自定义容器模板中的最后一个allocato

(转)java内存泄漏的定位与分析

转自:http://blog.csdn.net/x_i_y_u_e/article/details/51137492 1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题. 编写java程序最为方便的地方就是我们不需要管理内存的分配和释放,一切由jvm来进行处理,当java对象不再被应用时,等到堆内存不够用时,jvm会进行垃圾回收,清除这些对象占用的堆内存空间,如果对象一直被应用,jvm无法对其进行回收,创建新的对象时

java内存泄漏的定位与分析

1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题. 编写java程序最为方便的地方就是我们不需要管理内存的分配和释放,一切由jvm来进行处理,当java对象不再被应用时,等到堆内存不够用时,jvm会进行垃圾回收,清除这些对象占用的堆内存空间,如果对象一直被应用,jvm无法对其进行回收,创建新的对象时,无法从Heap中获取足够的内存分配给对象,这时候就会导致内存溢出.而出现内存泄露的地方,一般是不断的往容器中存放对象

实战Java内存泄漏问题分析 -- hazelcast2.0.3使用时内存泄漏 -- 1

公司当年有一个自己缓存集群用户session的Java library,是基于hazlcast2.0.3实现的,最近在customer site集群环境中某个blade报了Out of Memory Exception, 其他blades都正常,马上用jrockit jrcmd命令dump了堆和线程进行分析. printf "##################### heap ##################\n" su -p occas -c "/opt/jrocki

实战Java内存泄漏问题分析 -- hazelcast2.0.3使用时内存泄漏 -- 2

hazelcast 提供了3中方法调用startCleanup: 第一种是在ConcuurentMapManager的构造函数中,通过调用node的executorManager中的ScheduledExecutorService来创建每秒执行一次cleanup操作的线程(代码如下).由于这是ConcuurentMapManager构造函数的代码,所以这种调用startCleanup的操作是默认就会有的. node.executorManager.getScheduledExecutorServ

Java内存泄漏分析系列之一:使用jstack定位线程堆栈信息

原文地址:http://www.javatang.com 前一段时间上线的系统升级之后,出现了严重的高CPU的问题,于是开始了一系列的优化处理之中,现在将这个过程做成一个系列的文章. 基本概念 在对Java内存泄漏进行分析的时候,需要对jvm运行期间的内存占用.线程执行等情况进行记录的dump文件,常用的主要有thread dump和heap dump. thread dump 主要记录JVM在某一时刻各个线程执行的情况,以栈的形式显示,是一个文本文件.通过对thread dump文件可以分析出

Java内存泄漏的几种可能

Java内存泄漏引起的原因: 内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏. 长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景. 造成内存泄漏的几种情况: 1.静态集合类引起内存泄漏 像HashMap.Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对

Java 内存泄漏--全解析和处理办法 [ 转载 ]

Java内存泄露——全解析和处理办法 [转载] @author 小筐子 @address http://www.jianshu.com/p/bf159a9c391a 本文章会一步一步的探讨内存泄露的问题.博主第一次书写长篇技术贴,如有错误或不周到的地方请多指教. JAVA是垃圾回收语言的一种,开发者无需特意管理内存分配.但是JAVA中还是存在着许多内存泄露的可能性,如果不好好处理内存泄露,会导致APP内存单元无法释放被浪费掉,最终导致内存全部占据堆栈(heap)挤爆进而程序崩溃. 内存泄露 说到