一、Java内存泄露例子
Vector v = new Vector( 10 ); for ( int i = 1 ;i < 100 ; i ++ ){ Object obj = new Object(); v.add(obj); obj = null ; }
在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 obj 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 obj 引用置空。问题是当 obj 引用被置空后,如果发生 GC ,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管 obj 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。
在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度。我们有时也将其称为“对象游离”。
public class FileSearch{ private byte [] content; private File mFile; public FileSearch(File file){ mFile = file; } public boolean hasString(String str){ int size = getFileSize(mFile); content = new byte [size]; loadFile(mFile, content); String s = new String(content); return s.contains(str); } }
在这段代码中,FileSearch 类中有一个函数 hasString ,用来判断文档中是否含有指定的字符串。流程是先将mFile 加载到内存中,然后进行判断。但是,这里的问题是,将 content 声明为了实例变量,而不是本地变量。于是,在此函数返回之后,内存中仍然存在整个文件的数据。而很明显,这些数据我们后续是不再需要的,这就造成了内存的无故浪费。
案例1
代码
public static void main(String[] array) throws InterruptedException { List<MyObj> list = new ArrayList<MyObj>(); int i =0; while(true) { list.add(new MyObj("Str"+i,i++)); } } class MyObj { public String value; public int i; public byte[] b; public MyObj(String value, int i) { this.value = value; this.i = i; b=new byte[1024*1024]; } }
命令:
C:WindowsSystem32>jps -l 20592 21092 sun.tools.jps.Jps 11024 16268 jconsole.MemoryTest
<pre name="code" class="java">C:WindowsSystem32>jmap -dump:file=16268.bin 16268 Dumping heap to C:WindowsSystem3216268.bin ... Heap dump file created
将文件16268导入 Memory Analysis Tool
分配了大量的MyObj 对象。结合代码去查找问题,很快就能找到问题。
案例2
MySQL jdbc 5.1.6里,默认情况下,如果一个Connection永远不掉用close,即使你每一个Statement, ResultSet都调用了close,仍然会有内存泄漏,换句话说,Statement的close没有把自己的资源释放干净,Statement会在对应的Connection里有缓存 在我们的项目中采用了数据库链接池的技术,我们的数据库链接应用完成后不是马上调用close方法关闭掉,而是返回给了数据库链接池管理。所以链接池中的活动connettion对象中实际上持有了Statement的引用,造成内存泄露。
通过图中,MAT给出了关于本次检测有两处占用内存较多的疑似泄露,下面是详细说明
先从26%的嫌疑人suspect2看起,报告很直观的给出系统中有13个关于数据库链接的引用对象占用了59.83%的内存资源,我们还可以点击details查看一下详细情况
可以看到这13个数据库链接引用其实就是数据库链接池的数据库链接对象。而数据库链接对象本身仅占用100K左右内存,因此不可能达到26M的内存占用量,所以基本可以断定是数据库链接占用了原查询过程中的一些结果集等对象的引用,造成内存泄露问题。
案例3、
quartz 内存溢出
案例4、持久代溢出
public class OOMPermTest { public static void main(String[] args){ oom(); } private static void oom(){ Object[] array = new Object[10000000]; for(int i=0; i<10000000; i++){ String d = String.valueOf(i).intern(); array[i]=d; } }
intern() 将撑破持久代。