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

 这将会是一篇比较邪恶的文章,当你想在某个人的生活中制造悲剧时你可能会去google搜索它。在Java的世界里,内存溢出仅仅只是你在这种情况下可能会引入的一种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
内存溢出
java 内存溢出、java 内存溢出分析、java内存溢出怎么解决、java内存溢出分析工具、java内存溢出的原因,以便于您获取更多的相关知识。

时间: 2025-01-30 10:53:18

编写Java代码制造一个内存溢出的情况_java的相关文章

VoltDB对Java代码使用一个内存型的、高性能的数据库

过去几年来,出现了一种称为 NoSQL 的新型数据库管理系统.设计这些数据存储是为了克服在扩展传统http://www.aliyun.com/zixun/aggregation/22.html">关系数据库来处理一些应用程序时必须处理的数据负载类型的难题,比如说 Amazon.这种可伸缩性的实现需要一定的代价:NoSQL 系统通常不符合 ACID(原子性.一致性.隔离和耐久性):它们最终一致地表明,只要给定一定量的时间,所有数据更新最终都会通过该系统传播.这不符合某些类型的应用程序的要求.

VoltDB简介:对 Java 代码使用一个内存型的、高性能的数据库

简介 过去几年来,出现了一种称为 NoSQL 的新型数据库管理系统.设计这些数据存储是为了克服在扩展传统关 系数据库来处理一些应用程序时必须处理的数据负载类型的难题,比如说 Amazon.这种可伸缩性的实现需要 一定的代价:NoSQL 系统通常不符合 ACID(原子性.一致性.隔离和耐久性):它们最终一致地表明,只要 给定一定量的时间,所有数据更新最终都会通过该系统传播.这不符合某些类型的应用程序的要求. 过去用于在线事务处理 (OLTP) 的关系数据库管理系统确实提供了一致性保证(它们符合 A

java中三种常见内存溢出错误的处理方法(good)

相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识. 在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识.jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域).Heap space(堆区域).Java Stacks(Java栈).其中永久保存区域主要存放Class

Java上传数据内存溢出

问题描述 Java上传数据内存溢出 银行的系统,上传一个文件给其他系统,由于数据量太大,报内存溢出,急求. 解决方案 增加内存吧.银行有的是钱. 解决方案二: 上传文件时通常是将文件流作为字节流读取到内存中,再生成服务上的目标文件并写入的,所以可以批量读取,批量写入来取代一次性全部读取. 解决方案三: 流式读取文件,可以解决这个问题

android-用 java 代码获得一个自定义属性的值

问题描述 用 java 代码获得一个自定义属性的值 我在 attrs.xml 文件中创建了一个属性: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="Custom"> <attr name="src" format="integer" /> </dec

界面-java代码 写一个班级管理系统

问题描述 java代码 写一个班级管理系统 在eclipse里面写一个班级信息管理系统的图形界面 首先显示主界面 课程信息管理系统和学生信息管理系统和退出 进入课程信息管理界面 可以进行课程信息的修改.增加.查询.删除.返回主界面.退出: 进入学生信息管理界面 可以进行学生信息的修改.增加.查询.删除.返回主界面.退出: 解决方案 在这种情况下.我觉得你可以去源码网站找,一大堆得兄弟.源码天空?快去吧. 解决方案二: http://blog.sina.com.cn/s/blog_4e97cd62

java-如何通过Java代码判断一个IMEI号是正确的!非常感谢!!!

问题描述 如何通过Java代码判断一个IMEI号是正确的!非常感谢!!! 如何通过一个Java方法实现判断IMEI号的正确性,不用正则表达式. 根据接收到的IMEI号验证它是一个IMEI号而非随便写的一个号码. 网上有好多验证手机号码,身份证号码,邮箱号码等等等的验证,好像就没怎么搜索到验证IMEI号的. 解决方案 网上有好多验证手机号码,身份证号码,邮箱号码等等等的验证 其实这些验证也只是格式上.形式上的验证,而不是实际的验证.实际要想知道IMEI号,包括电话.身份证.邮箱等等,都需要和对应的

java程序运行时内存分配详解_java

一. 基本概念    每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一对应一个堆,每一个线程有一个自己私有的栈.进程所创建的所有类的实例(也就是对象)或数组(指的是数组的本身,不是引用)都放在堆中,并由该进程所有的线程共享.Java中分配堆内存是自动初始化的,即为一个对象分配内存的时候,会初始化这个对象中变量.虽然Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在栈中分配,也

深入解读Java代码组织中的package包结构_java

如果我们在Class对象上调用getPackage方法,就可以得到描述该类所在包的Package对象(Package类是在java.lang中定义的).我们也可以用包名通过调用静态方法getPackage或者调用静态方法getPackages(该方法返回由系统中所有已知包构成的数组)来获得Package对象.getName方法可以返回包的全名. Package对象的使用与其他反射类型完全不同,即我们不能在运行时创建或操纵包.我们可以使用Package对象来获取有关包的信息,诸如包的用途.谁创建了