• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

编写Java代码制造一个内存溢出的情况

java 搞代码 4年前 (2022-01-05) 161次浏览 已收录 0个评论

这篇文章主要介绍了编写Java代码制造一个内存溢出的情况,或许这种有意制造能够更好地帮助理解Java中的内存溢出情况XD 需要的朋友可以参考下

 这将会是一篇比较邪恶的文章,当你想在某个人的生活中制造悲剧时你可能会去google搜索它。在Java的世界里,内来源[email protected]搞@^&代*@码)网存溢出仅仅只是你在这种情况下可能会引入的一种bug。你的受害者会在办公室里度过几天甚至是几周的不眠之夜。

在这篇文章中我将会介绍两种溢出方式,它们都是比较容易理解和重现的。并且它们都是来源现实项目的案例研究,但是为了让你清晰地掌握,我把它们简化了。

不过放心,在我们遇到和解决了很过溢出bug之后,类似的案例将会比你想象得更加普遍。

先来一个进入状态的,在使用HashSet/HashMap时,所用键值没有或者其equals()/hashCode()方法不正确,这会导致一个臭名昭著的错误。
 

 class KeylessEntry { static class Key { Integer id; Key(Integer id) { this.id = id; } @Override public int hashCode() { return id.hashCode(); } } public static void main(String[] args) { Map m = new HashMap(); while (true) for (int i = 0; i <10000; i++) if (!m.containsKey(i)) m.put(new Key(i), "Number:" + i); } } 

当你运行上面的代码时,你可能会期望它运行起来永远不会出问题,毕竟内置的缓存方案只会增加到10,000个元素,然后就不会再增加了,所有的key都已经出现在 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; } 

下一个你得提醒朋友的是和String处理相关的操作。它的表现会很诡异,特别是结合JVM版本差异的时候。String的内部工作机制在 JDK 7u6中被改变了,所以如果你发现产品环境只是小版本号的区别,那么你已经准备好条件了。把类似下面的代码给你的朋友调试,然后问他为什么这个bug只会在产品中出现。
 

 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 substrings = new ArrayList(); for(int i=0; i<100; i++){ String longStr = createLongString(MB); String subStr = longStr.substring(1,10); substrings.add(subStr); } } } 

上面的代码出了什么问题呢?当它在JDK 7u6之前的版本上运行的时候,返回的字符串将会保存一个对那个1M左右大小的字符串的引用,如果你运行的时候设置为-Xmx100m,你会得到一个意想不到的oom错误。结合你实验环境中平台和版本的差异,伤脑经的事情就产生了。

现在如果你想掩盖你的足迹,我们可以引进一些更加高级的概念。比如

  •     在不同的类加载器中载入有破坏性的代码,在加载的类被原始类加载器删除后保持对它的引用,可以模拟一个类加载器溢出
  •     把攻击性的代码隐藏在finalize方法中,使得程序表现变得不可预测
  •     在一个长期运行的线程中加入棘手的组合,它可能在ThreadLocals中保存了一些可以被线程池访问的东西,以便管理应用线程。

我希望我们给了你一些思考的原材料以及当你想修理某人时的一些素材。这将带来无穷无尽的调试。除非你的朋友使用 Plumbr来查找溢出的所在地。

以上就是编写Java代码制造一个内存溢出的情况的详细内容,更多请关注gaodaima搞代码网其它相关文章!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:编写Java代码制造一个内存溢出的情况

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址