如何用Java编写一段代码引发内存泄露
作者:网络转载 发布时间:[ 2015/4/7 11:07:48 ] 推荐标签:Java 代码 线程
文本来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码。
Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码。这个问题我一点思路都没有,好?。
A1:通过以下步骤可以很容易产生内存泄露(程序代码不能访问到某些对象,但是它们仍然保存在内存中):
应用程序创建一个长时间运行的线程(或者使用线程池,会更快地发生内存泄露)。
线程通过某个类加载器(可以自定义)加载一个类。
该类分配了大块内存(比如new byte[1000000]),在某个静态变量存储一个强引用,然后在ThreadLocal中存储它自身的引用。分配额外的内存new byte[1000000]是可选的(类实例泄露已经足够了),但是这样会使内存泄露更快。
线程清理自定义的类或者加载该类的类加载器。
重复以上步骤。
由于没有了对类和类加载器的引用,ThreadLocal中的存储不能被访问到。ThreadLocal持有该对象的引用,它也持有了这个类及其类加载器的引用,类加载器持有它所加载的类的所有引用,这样GC无法回收ThreadLocal中存储的内存。在很多JVM的实现中Java类和类加载器直接分配到permgen区域不执行GC,这样导致了更严重的内存泄露。
这种泄露模式的变种之一是如果你经常重新部署以任何形式使用了ThreadLocal的应用程序、应用容器(比如Tomcat)会很容易发生内存泄露(由于应用容器使用了如前所述的线程,每次重新部署应用时将使用新的类加载器)。
A2:
静态变量引用对象
class MemorableClass {
static final ArrayList list = new ArrayList(100);
}
调用长字符串的String.intern()
String str=readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you cant remove
str.intern();
try {
BufferedReader br = new BufferedReader(new FileReader(inputFile));
...
...
} catch (Exception e) {
e.printStacktrace();
}
未关闭连接
try {
Connection conn = ConnectionFactory.getConnection();
...
...
} catch (Exception e) {
e.printStacktrace();
}
JVM的GC不可达区域
比如通过native方法分配的内存。
web应用在application范围的对象,应用未重启或者没有显式移除
getServletContext().setAttribute(“SOME_MAP”, map);
web应用在session范围的对象,未失效或者没有显式移除
session.setAttribute(“SOME_MAP”, map);
不正确或者不合适的JVM选项
比如IBM JDK的noclassgc阻止了无用类的垃圾回收
A3:如果HashSet未正确实现(或者未实现)hashCode()或者equals(),会导致集合中持续增加“副本”。如果集合不能地忽略掉它应该忽略的元素,它的大小只能持续增长,而且不能删除这些元素。

sales@spasvo.com