这将是一个相当邪恶的职位-当您确实希望使某人的生活陷入困境时,您将在谷歌上搜索。 在Java开发领域,内存泄漏只是您在这种情况下会引入的错误类型。 为您的受害者保证几天甚至几周的办公室不眠之夜。
我们将在这篇文章中描述两次泄漏。 两者都很容易理解和复制。 泄漏源于现实世界中的案例研究,但为清楚起见,我们提取了演示案例,以使您更短,更简单。 但是请放心–在我们已经看到并修复了数百个泄漏之后–与演示的情况类似的情况比您预期的更普遍。
第一个闯入环的参赛者–臭名昭著的HashSet / HashMap解决方案,其中使用的键不具有或具有不正确的equals()/ hashCode( )解决方案。
class KeylessEntry {static class Key {Integer id;Key(Integer id) {this.id = id;}@Overridepublic int hashCode() {return id.hashCode();}}public static void main(String[] args) {Map<key, string=""> m = new HashMap<key, string="">();while (true)for (int i = 0; i < 10000; i++)if (!m.containsKey(i))m.put(new Key(i), "Number:" + i);}
}
当您执行上面的代码时,您希望它可以永远运行而不会出现任何问题-毕竟,内置的幼稚缓存解决方案应该只扩展到10,000个元素,然后停止增长,因为所有键都已经存在于HashMap中 。 但是,情况并非如此-元素会继续添加,因为Key类在其hashCode()旁边不包含适当的equals()实现 。 该解决方案很容易–与以下示例类似,添加equals()方法的实现,您很高兴。 但是,在设法找到原因之前,您肯定已经失去了一些宝贵的脑细胞。
@Override
public boolean equals(Object o) {boolean response = false;if (o instanceof Key) {response = (((Key)o).id).equals(this.id);}return response;
}
让您的朋友保持清醒的第二个问题-一些操作中的字符串处理。 像魅力一样工作,尤其是与JVM版本差异结合使用时。 在JDK 7u6中更改了 String内部构件的工作方式,因此,如果您设法找到仅次要版本导致生产和登台不同的环境,那么您已经准备就绪 。 向您的朋友投掷类似于以下代码的代码,以进行调试,并想知道为什么问题不在生产中出现在其他任何地方。
class Stringer {static final int MB = 1024*512;static String createLongString(int length){StringBuilder sb = new StringBuilder(length);for(int i=0; i < length; i++)sb.append('a');sb.append(System.nanoTime());return sb.toString();}public static void main(String[] args){List<string> substrings = new ArrayList<string>();for(int i=0; i< 100; i++){String longStr = createLongString(MB);String subStr = longStr.substring(1,10);substrings.add(subStr);}}
}
上面的代码中发生的事情–当它在预JDK 7u6上运行时,返回的子字符串在下面保留了对〜1MB大字符串的引用。 因此,当使用-Xmx100m运行示例时,您将遇到意外的OutOfMemoryException。 将此与平台差异结合起来,在您正在尝试的环境中使用不同的JDK版本,第一批白发将蓬勃发展。 现在,如果您想掩盖自己的足迹,我们可以将一些更高级的概念添加到产品组合中,例如
- 在其他类加载器中加载已损坏的代码,并在丢弃原始类加载器后模仿类加载器泄漏 ,保留对加载的类的引用。
- 将有问题的代码隐藏到finalize()方法中,使症状真正不可预测
- 将长时间运行的线程的棘手组合扔掉,这些线程将某些东西存储在ThreadPool所访问的ThreadLocals中–受控制的应用程序线程
希望我们能给您带来一些思考,并为您下次生气时提供一些技巧。 无数小时的核心调试保证。 除非您的朋友使用Plumbr ,否则当然会为他找到泄漏。 但是公然的市场营销方面,我希望我们能够在两个简单的例子中证明在Java中创建内存泄漏有多么容易。 你们中的大多数人都经历了追踪这样一个错误的难度。 因此,如果您喜欢这篇文章,请订阅我们的Twitter feed,以得到有关我们未来有关JVM性能调整的内容的警报。
翻译自: https://www.javacodegeeks.com/2013/04/how-to-create-a-memory-leak.html