我刚刚接受了一次采访,被要求用Java创建内存泄漏。
不用说,我觉得自己很傻,不知道如何开始创作。
什么样的例子?
我刚刚接受了一次采访,被要求用Java创建内存泄漏。
不用说,我觉得自己很傻,不知道如何开始创作。
什么样的例子?
当前回答
对前面的答案有一点改进(为了更快地生成内存泄漏),就是使用从大型XML文件加载的DOM文档实例。
其他回答
我曾经有过一次关于PermGen和XML解析的“内存泄漏”。我们使用的XML解析器(我记不清是哪一个)对标记名执行String.intern(),以加快比较速度。我们的一位客户有一个好主意,不将数据值存储在XML属性或文本中,而是将其存储为标记名,因此我们有了这样一个文档:
<data>
<1>bla</1>
<2>foo</>
...
</data>
事实上,他们没有使用数字,而是使用更长的文本ID(约20个字符),这些ID是唯一的,每天的使用率为1000万至1000万。这使得每天有200 MB的垃圾,而这些垃圾再也不需要了,也永远不会被GCed(因为它在PermGen中)。我们将permagen设置为512MB,因此内存不足异常(OOME)需要大约两天的时间才能到达。。。
保存对象引用的静态字段(尤其是最终字段)
class MemorableClass {
static final ArrayList list = new ArrayList(100);
}
(未关闭)开放流(文件、网络等)
try {
BufferedReader br = new BufferedReader(new FileReader(inputFile));
...
...
} catch (Exception e) {
e.printStackTrace();
}
未封闭的连接
try {
Connection conn = ConnectionFactory.getConnection();
...
...
} catch (Exception e) {
e.printStackTrace();
}
JVM垃圾收集器无法访问的区域,例如通过本机方法分配的内存。
在web应用程序中,某些对象存储在应用程序范围中,直到应用程序被显式停止或删除。
getServletContext().setAttribute("SOME_MAP", map);
不正确或不适当的JVM选项,例如IBM JDK上的noclassgc选项,它阻止未使用的类垃圾收集
请参阅IBM JDK设置。
JDK 1.7之前内存泄漏的实时示例:
假设您读取了一个包含1000行文本的文件,并将其保存在String对象中:
String fileText = 1000 characters from file
fileText = fileText.subString(900, fileText.length());
在上面的代码中,我最初读取了1000个字符,然后执行了子字符串,只获得最后100个字符。现在,fileText应该只引用100个字符,所有其他字符都应该被垃圾收集,因为我丢失了引用,但是在JDK1.7之前,substring函数间接引用了最后100个字符的原始字符串,并阻止了整个字符串的垃圾收集,而整个1000个字符将一直保存在内存中,直到您丢失了对子字符串的引用。
您可以创建一个类似于上述的内存泄漏示例。
内存泄漏是一种资源泄漏,当计算机程序错误地管理内存分配,导致不再需要的内存无法释放时,就会发生这种情况=>维基百科定义
这是一种相对基于上下文的主题,你可以根据自己的喜好创建一个主题,只要未使用的引用永远不会被客户使用,但仍然存在。
第一个例子应该是一个自定义堆栈,而不取消有效Java第6项中过时的引用。
当然,只要你愿意,还有很多,但如果我们看看Java内置类,它可能是
子列表()
让我们检查一些超级愚蠢的代码来产生泄漏。
public class MemoryLeak {
private static final int HUGE_SIZE = 10_000;
public static void main(String... args) {
letsLeakNow();
}
private static void letsLeakNow() {
Map<Integer, Object> leakMap = new HashMap<>();
for (int i = 0; i < HUGE_SIZE; ++i) {
leakMap.put(i * 2, getListWithRandomNumber());
}
}
private static List<Integer> getListWithRandomNumber() {
List<Integer> originalHugeIntList = new ArrayList<>();
for (int i = 0; i < HUGE_SIZE; ++i) {
originalHugeIntList.add(new Random().nextInt());
}
return originalHugeIntList.subList(0, 1);
}
}
实际上,还有另一个技巧,我们可以利用HashMap的查找过程,使用HashMap造成内存泄漏。实际上有两种类型:
hashCode()始终相同,但equals()不同;使用随机hashCode()和equals()始终为true;
Why?
hashCode()->bucket=>equals()来定位该对
我打算先提到substring(),然后再提到subList(),但这个问题似乎已经解决了,因为它的源代码在JDK8中。
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
每个人都会忘记本机代码路径。以下是泄漏的简单公式:
声明本机方法。在本机方法中,调用malloc。不要打免费电话。调用本机方法。
记住,本机代码中的内存分配来自JVM堆。