今天爱分享给大家带来Java 中会存在内存泄漏吗,请简单描述。【面试题详解】,希望能够帮助到大家。
【参考答案】
所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java 中
有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象变成了孤儿的时候,对象将
自动被垃圾回收器从内存中清除掉。由于 Java 使用有向图的方式进行垃圾回收管理,可以
消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么 GC
也是可以回收它们的,例如下面的代码可以看到这种情况的内存回收:
package com.huawei.interview; import java.io.IOException; public class GarbageTest { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub try { gcTest(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("has exited gcTest!"); System.in.read(); System.in.read(); System.out.println("out begin gc!"); for(int i=0;i<100;i++) { System.gc(); System.in.read(); System.in.read(); } } private static void gcTest() throws IOException { System.in.read(); System.in.read(); Person p1 = new Person(); System.in.read(); System.in.read(); Person p2 = new Person(); p1.setMate(p2); p2.setMate(p1); System.out.println("before exit gctest!"); System.in.read(); System.in.read(); System.gc(); System.out.println("exit gctest!"); } private static class Person { byte[] data = new byte[20000000]; Person mate = null; public void setMate(Person other) { mate = other; } } }
java 中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内
存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致
不能被回收,这就是 java 中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个
对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被
垃圾回收器回收的,这就是 java 中可能出现内存泄露的情况,例如,缓存系统,我们加载
了一个对象放在缓存中(例如放在一个全局 map 对象中),然后一直不再使用它,这个对象一
直被缓存引用,但却不再被使用。
检查 java 中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某
个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。
如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引
用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外
部类对象将不会被垃圾回收,这也会造成内存泄露。
[] 是对象已不可到达,而内存又没有回收,真正的内存黑洞。
而 Java 的泄漏,则是因为各种原因,对象对应用已经无用,但一直被持有,一直可到达。
总结原因无外乎几方面:
1. 被生命周期极长的集合类不当持有,号称是 Java 内存泄漏的首因。
这些集合类的生命周期通常极长,而且是一个辅助管理性质的对象,在一个业务事
务运行完后,如果没有将某个业务对象主动的从中清除的话,这个集合就会吃越来
越多内存,可以用 WeakReference,如 WeakHashMap,使得它持有的对象不增加对象
的引用数。
2. Scope 定义不对,这个很简单了,方法的局部变量定义成类的变量,类的静态变量
等。
3. 异常时没有加 finally{}来释放某些资源,JDBC 时代也是很普遍的事情。
4. 另外一些我了解不深的原因,如:Swing 里的 Listener 没有显式 remove;内部类持
有外部对象的隐式引用;Finalizers 造成关联对象没有被及时清空等。
内存泄漏的检测
有不少工具辅助做这个事情的,如果手上一个工具也没有,可以用 JDK 自带的小工具:
看看谁占满了 Heap?
用 JDK6 的 jmap 可以显示运行程序中对象的类型,个数与所占的大小
先用 jps 找到进程号,然后 jmap -histo pid 显示或 jmap
-dump:file=heap_file_name pid 导出 heap 文件
为什么这些对象仍然可以到达?
用 jhat(Java Heap Analysis Tool) 分析刚才导出的 heap 文件。
先 jhat heap_file_name,然后打开浏览器 http://localhost:7000/ 浏览。