关于内存溢出问题,java.lang.OutOfMemoryError: Java heap space (此处是 运行时 String长度过长,总占用内存 过多)
报错信息
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at label.label01.main(label01.java:66)
具体代码
出错代码
ArrayList<Hero> hash = new ArrayList<>();
long start = System.currentTimeMillis();
StringBuilder random = new StringBuilder(); // 创建一个随机数缓存
Random random1 = new Random();
for (int i = 0; i < 3000000; i++) {
//生成4位随机数,此处循环若在上一层for里会产生内存溢出
for (int i1 = 0; i1 < 4; i1++) {
random.append(random1.nextInt(10)) ;
}
hash.add(new Hero("hero-"+random));
}
hash.forEach(hero -> {
if (hero.name.equals("hero-5555"))
hash.indexOf(hero);
System.out.println(hero.name);
});
long end = System.currentTimeMillis();
System.out.println(end-start);
修改后1:
ArrayList<Hero> hash = new ArrayList<>();
long start = System.currentTimeMillis();
for (int i = 0; i < 3000000; i++) {
//采用,新的生成4位随机数方法
int j=(int)(Math.random() * 9000) + 1000;
hash.add(new Hero("hero-"+j));
}
hash.forEach(hero -> {
if (hero.name.equals("hero-5555"))
hash.indexOf(hero);
System.out.println(hero.name);
});
long end = System.currentTimeMillis();
System.out.println(end-start);
修改后2:
ArrayList<Hero> hash = new ArrayList<>();
long start = System.currentTimeMillis();
StringBuilder random = new StringBuilder(); // 创建一个随机数缓存
Random random1 = new Random();
for (int i = 0; i < 3000; i++) {
//生成4位随机数,此处循环若在上一层for里会产生内存溢出----此处见解有点狭义,请看下一步
for (int i1 = 0; i1 < 4; i1++) {
random.append(random1.nextInt(10)) ;
}
//具体错因:random在append东西之后没有清空,一直加,导致不断边长,然后此处的Hero对象中的name属性(String)长度过长,导致jvm内存溢出。
hash.add(new Hero("hero-"+random));
random.setLength(0); //----------新增了一个清空-----------------------------
}
hash.forEach(hero -> {
if (hero.name.equals("hero-5555"))
hash.indexOf(hero);
System.out.println(hero.name);
});
long end = System.currentTimeMillis();
System.out.println(end-start);
具体出错原因:
我们先看一下String中的容纳大小

最大值是:2的31次方减一
明显此处的语句是在for循环中,所以是在运行中超过了int的限制,
就是字符串总大小超过了2^31 -1,也就是超过了4G
hash.add(new Hero("hero-"+random));
问:字符串有长度限制吗?是多少?
答:首先字符串的内容是由一个字符数组 char[] 来存储的,由于数组的长度及索引是整数,且String类中返回字符串长度的方法length() 的返回值也是int ,所以通过查看java源码中的类Integer我们可以看到Integer的最大范围是2^31 -1,由于数组是从0开始的,所以数组的最大长度可以使【0~2^31】通过计算是大概4GB。
但是通过翻阅java虚拟机手册对class文件格式的定义以及常量池中对String类型的结构体定义我们可以知道对于索引定义了u2,就是无符号占2个字节,2个字节可以表示的最大范围是2^16 -1 = 65535。
其实是65535,但是由于JVM需要1个字节表示结束指令,所以这个范围就为65534了。超出这个范围在编译时期是会报错的,但是运行时拼接或者赋值的话范围是在整形的最大范围。
简单地说
字符串有长度限制,在编译期,要求字符串常量池中的常量不能超过65535,并且在javac执行过程中控制了最大值为65534。
在运行期,长度不能超过Int的范围,否则会抛异常。
浙公网安备 33010602011771号