Java的内存泄露

标签: java 内存泄露 | 发表时间:2014-05-06 17:49 | 作者:
出处:http://it.deepinmind.com
Java有垃圾回收,因此不会出现内存泄露。 大错特错。

这个说法存在好几个问题。尽管Java的确有垃圾回收器来回收那些不用的内存块,但你不要指望它能够点铁成金。GC减轻了开发人员肩上的负担,而原本的那些工作非常容易出错,不过并不是所有内存分配的问题它都能够解决。更糟糕的是,Java的设计允许它可以欺骗GC,使得它能够保留一些程序已经不再使用的内存。经历了20年的C开发以及7年的Java开发后(中间有重叠),我敢说,在这方面Java绝对是远比C/C++要好。尽管它仍有改进的空间。在这些改进成为现实之前,作为开发人员最好能了解下内存处理的基本原理以及一些常见的坑,以免栽到里面去。但首先,

什么是内存泄露?

内存泄露是指程序不停地分配内存,但不再使用的时候却没有释放掉它,这会导致本来就有限的内存的占用量出现飙升,并且这不受程序控制,最终导致程序的运行变慢。

在那些美好的C语言开发的时代,我们说的内存泄露是指程序遗失了某个内存段的引用而没有释放掉它。这种情况下,程序获取不到这个内存区域的句柄或者指针,也无法调用free函数来释放掉它,因此这个内存块会一直处于分配的状态,没法被程序重用,这样就造成了内存的浪费。当然了,程序退出的话,操作系统会回收掉这块内存的。

这是个非常典型的内存泄露,不过我上面给出的定义更广泛一些。还有一种情况是代码仍旧拥有这块内存的指针,尽管现在这块内存已经不用了,但程序也不去释放它。就比如说一个程序员创建了一个链表,把所有通过malloc分配的内存指针全存了进去,但他从来不去调用free函数释放掉它们。结果也是一样的。既然结果是一样的,能不能获取到释放内存的指针也不那么重要了,因为你根本就不去释放它。这只是影响到了解决问题的方式,不过不管是哪种情况,修复BUG总是得修改代码的。

如果我们来看下Java和它的GC,你会发现经典的那个由于释放了内存引用导致无法释放内存的那种情况几乎不可能发生。如果是那样的话GC判断出分配的内存的所有引用已经释放掉了就自己去释放内存了。事实上,这也是Java里面标准的释放内存的方法:你只需不再引用某个对象就可以了,GC会去回收它的。这里没有垃圾桶,也没有分类垃圾箱(不需要你去扔垃圾)。别管它就行了,垃圾回收器会去回收它的。这也正是很多开发人员认为Java不存在内存泄露的原因。从实际的角度来看这的确几乎是正确的:和使用C/C++或者其它没有垃圾回收器的语言相比,使用Java开发内存泄露的麻烦事的确少了不少。

我们终于要说到重点了:Java里面是如何发生内存泄露的?

线程以及线程本地存储就非常容易产生内存泄露。通过下面的五个步骤可以很容易地产生内存泄露:

  1. 应用程序创建一个长时间运行的线程(或者使用线程池,那样泄露会更快一些)。
  2. 线程通过某个类加载器加载了一个类。
  3. 这个类分配了一大块内存(比如,new byte[1000000]),并且在它的静态字段中存储了一个强引用,然后在ThreadLocal中存储它自身的一个引用。额外分配的这个内存(new byte[1000000])其实是多余的(类实例的泄露就足够了),不过这样能使内存泄露的速度更快一些。
  4. 线程清理了自定义的类以及加载它的类加载器的所有引用。
  5. 重复以上步骤。

因为你已经没有这个类以及它的类加载器的引用了,也就不能再访问它的ThreadLocal中的存储了,因此你也就无法访问分配的这块内存(除非你开始用反射来获取)。但是这个ThreadLocal的存储还存在引用,因此GC无法回收这块内存。这个线程本地的存储不是弱引用(顺便提一句,为什么不用弱引用?)

如果你从来没有类似的经验,你可能会想,这得多脑残才能搞出这么极端的一个场景。但事实上,上述这种泄露的模式是非常自然的(好吧,程序员们,你们可别故意这么搞 ),当你在Tomcat上调试自己的程序的时候就会出现这样的泄露。对Java来说这太正常了。重新部署应用但却不重启Tomcat实例,这通常会导致内存越来越少,这正是发生了上述的这种泄露,很少有Tomcat能够避免这种情况。应用程序应当谨慎地使用ThreadLocal。

使用静态变量存储大块数据时也应当同样小心。最好避免使用静态变量,除非你很相信这个运行你程序的容器不会发生泄露。这些容器的类加载的层次结构和Java的比起来要灵活多了。如果你把大量的数据存储到一个Map或者Set里,为什么不使用它们的弱引用的版本呢?如果KEY都没了,还需要关联的那个值干嘛?

现在来说下HashMap和HashSet。如果你使用了没有实现或者错误地实现了eqauls和hashCode方法的对象来作为KEY的话,调用put()方法会把你的数据扔向深渊。你再也没法恢复它了,更糟糕的是,每当你再放一个对象到这个集合中的时候,还会产生更多的副本。你把你的内存带上了一条不归路。

在Java中,还有许许多多的内存泄露的例子。尽管它们和C/C++相比,出现的频率要少得多。通常来说,有GC总比没有的要好。

原创文章转载请注明出处: Java的内存泄露

英文原文链接

相关 [java 内存泄露] 推荐:

Java的内存泄露

- - Java译站
Java有垃圾回收,因此不会出现内存泄露. 尽管Java的确有垃圾回收器来回收那些不用的内存块,但你不要指望它能够点铁成金. GC减轻了开发人员肩上的负担,而原本的那些工作非常容易出错,不过并不是所有内存分配的问题它都能够解决. 更糟糕的是,Java的设计允许它可以欺骗GC,使得它能够保留一些程序已经不再使用的内存.

诊断Java中的内存泄露

- - ImportNew
每次我怀疑有内存泄漏时,我都要翻箱倒柜找这些命令. 首先,我用下面的命令监视进程:. (如果有的话还有New Relic). 如果你看到内存上升很快,可能是因为虚拟机设置. 如果你没有明确指定JVM的内存设置,它将设置默认值给他们. 如果这些都不符合你所希望的,那么你就需要指定JVM的内存设置. 可以用下面的命令设置最小和最大堆大小:.

Java SE 6 故障排除指南 – 3、内存泄露

- - 码蜂笔记
如果你的应用程序执行的时间越来越长,或如果操作系统执行越来越慢,这可能是内存泄露的指示. 换句话说,虚拟内存被分配但在不需要时没有归还. 最终应用程序或系统没有可用内存,应用程序非正常终止. 这篇文章提供了一些涉及内存泄露的问题诊断的建议. 3.1 OutOfMemoryError 的含义. 一个最常见的内存泄露的指示是 java.lang.OutOfMemoryError 错误.

Java内存泄露的理解与解决

- - Java - 编程语言 - ITeye博客
转自: http://henryyang.iteye.com/blog/1188328. Java内存管理机制. 在C++ 语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期. 从申请分配、到使用、再到最后的释放. 这样的过程非常灵活,但是却十分繁琐,程序员很容易由于疏忽而忘记释放内存,从而导致内存的泄露.

如何用Java编写一段代码引发内存泄露

- - ImportNew
文本来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码. Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码. 这个问题我一点思路都没有,好囧. A1:通过以下步骤可以很容易产生内存泄露(程序代码不能访问到某些对象,但是它们仍然保存在内存中):.

Java 内存泄露的理解与解决过程

- - 极客521 | 极客521
本文详细地介绍了Java内存管理的原理,以及内存泄露产生的原因,同时提供了一些列解决Java内存泄露的方案,希望对各位Java开发者有所帮助. 在C++ 语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期. 从申请分配、到使用、再到最后的释放. 这样的过程非常灵活,但是却十分繁琐,程序员很容易由于疏忽而忘记释放内存,从而导致内存的泄露.

Java中的substring真的会引起内存泄露么?

- - 编程语言 - ITeye博客
Java中的substring真的会引起内存泄露么. 在Java中开发,String是我们开发程序可以说必须要使用的类型,String有一 个substring方法用来截取字符串,我们想必也常常使用. 但是你知道么,关于Java 6中的substring是否会引起内存泄露,在国外的论坛和社区有着一些讨论,以至于Java官方已经将其标记成bug,并且为此Java 7 还重新进行了实现.

java内存泄露和内存溢出区别

- - 互联网 - ITeye博客
虽然jvm可以通过GC自动回收无用的内存,但是代码不好的话仍然存在内存溢出的风险. 最近在网上搜集了一些资料,现整理如下:. 一、为什么要了解内存泄露和内存溢出. 1、内存泄露一般是代码设计存在缺陷导致的,通过了解内存泄露的场景,可以避免不必要的内存溢出和提高自己的代码编写水平;. 2、通过了解内存溢出的几种常见情况,可以在出现内存溢出的时候快速的定位问题的位置,缩短解决故障的时间.

Java常见问题分析(内存溢出、内存泄露、线程阻塞等)

- - Java - 编程语言 - ITeye博客
Java垃圾回收机制(GC) . 堆内存3代分布(年轻代、老年代、持久代) . ML(内存泄露) OOM(内存溢出)问题现象及分析 . IBM DUMP分析工具使用介绍. Java应用CPU、线程问题分析. Java垃圾回收机制(GC). 1.GC机制作用 . 1.1 JVM自动检测和释放不再使用的对象内存 .

关于内存泄露

- - 银河里的星星
valgrind 详细说明  http://www.cnblogs.com/wangkangluo1/archive/2011/07/20/2111273.html. 近期Imgsrc一处内存泄露问题的查找和解决  http://rdc.taobao.com/blog/cs/?p=1651.