`
zhanghong
  • 浏览: 90694 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

String 导致的内存溢出

    博客分类:
  • Java
阅读更多

String是java中经常使用的类,如果使用不当,也有可能出现内存泄露。例如执行以下代码就可能出现内存不够:

public class Test {
	private String large = new String(new char[100000]);

	public String getSubString() {
		return this.large.substring(0, 2);
	}

	public static void main(String[] args) {
		ArrayList<String> subStrings = new ArrayList<String>();
		for (int i = 0; i < 1000000; i++) {
			Test test = new Test();
			subStrings.add(test.getSubString());
		}
	}
}

 

我在机器上执行后出现以下错误:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.lang.String.<init>(Unknown Source)
    at com.bond.test.Test.<init>(Test.java:6)
    at com.bond.test.Test.main(Test.java:17)

 

这是为什么呢? subStrings 不是保存了1000000个长度为2的字符串吗,一个字符2个字节,总共不过40M 怎么会内存不够呢。其实不是这样的。

 

我们来看String中的subString方法:

 

public String substring (int start, int end) {
	if (start == 0 && end == count)
		return this;
	// NOTE last character not copied!
	// Fast range check.
	if (0 <= start && start <= end && end <= count) {
		return new String (offset + start, end - start, value);
	}
	throw new StringIndexOutOfBoundsException();
}

String (int start, int length, char[] data) {
	value = data;
	offset = start;
	count = length;
}

 

我们可以看到subString返回的String与原来的String实例共享同一个value,它们引用的同一个char数组。我们可以从下面代码得到答案:

 

		 String str1 = "sldkfknkldbjongekgds";
		 String str2 = str1.substring(3, 10);
		 Class c1 = str1.getClass();
		 Field f1 = c1.getDeclaredField("value");
		 f1.setAccessible(true);
		
		 Class c2 = str2.getClass();
		 Field f2 = c2.getDeclaredField("value");
		 f2.setAccessible(true);
		 System.out.println(f1.get(str1)==f2.get(str2));
		 System.out.println(Arrays.toString(((char[])(f2.get(str1)))));

 

结果:

true
[s, l, d, k, f, k, n, k, l, d, b, j, o, n, g, e, k, g, d, s]

可见虽然str2的长度为7,但是实际大小却和str1一样。

这个问题的解决办法就是new String(str.subString(int));来替代直接用subString 方法得到String实例:

public String getSubString() {
    return new String(this.large.substring(0,2));
  }

 这样创建的String的实际大小就只有2了。

5
5
分享到:
评论
5 楼 jag522 2014-09-23  
这个问题在JDK5,JDK6等老版本中存在,但是在JDK7,JDK8中已修复。
4 楼 jag522 2014-09-23  
subStrings 不是保存了1000000个长度为2的字符串吗,一个字符2个字节,总共不过40M 怎么会内存不够呢。

这里应该是3.8M左右吧,而不是40M。
3 楼 li5951680 2013-06-08  
miroku 写道
传递的数组是引用啊,怎么会产生多份相同大小的数组。而且,根据楼主的异常,可知是构造方法抛出的,所以,楼主的解释说不通

public class Test {
    private String large = new String(new char[100000]);

    public String getSubString() {
        return this.large.substring(0, 2);
    }

    public static void main(String[] args) {
        ArrayList<String> subStrings = new ArrayList<String>();
       Test test = new Test();
        for (int i = 0; i < 1000000; i++) {
           
            subStrings.add(test.getSubString());
        }
    }

这样写的话,就没事,说明和subString没关系,而是,你在循环里创建Test对象,每次都会创建一个large = new String(new char[100000]); 所以才会内存溢出


2楼说的很有道理,楼主这边日志太误导新人了.希望予以更正
2 楼 miroku 2011-02-22  
传递的数组是引用啊,怎么会产生多份相同大小的数组。而且,根据楼主的异常,可知是构造方法抛出的,所以,楼主的解释说不通

public class Test {
    private String large = new String(new char[100000]);

    public String getSubString() {
        return this.large.substring(0, 2);
    }

    public static void main(String[] args) {
        ArrayList<String> subStrings = new ArrayList<String>();
       Test test = new Test();
        for (int i = 0; i < 1000000; i++) {
           
            subStrings.add(test.getSubString());
        }
    }

这样写的话,就没事,说明和subString没关系,而是,你在循环里创建Test对象,每次都会创建一个large = new String(new char[100000]); 所以才会内存溢出
1 楼 Coder211 2011-02-22  
不错,文章写的很深入!
只是希望把问题解释的更清楚明白一些!

相关推荐

    NHibernate2.0.1 源码改进[SqlString内存不释放bug和MSSQL支持序列]

    NHibernate2.0.1 和NHibernate1.0.2 一直都存在一个问题,就是SqlString不释放,在系统大量Sql查询时, 会导致内存溢出 代码改进,使用最早最少淘汰策略使用SqlString缓存. 代码改进,增加MSSql支持序列

    safestringlib

    安全字符串库安全开发生命周期 (SDL) 建议禁止某些 C 库函数,因为它们会直接导致缓冲区溢出等安全漏洞。 然而,用于操作字符串和内存缓冲区的例程在软件和固件中很常见,并且对于完成某些编程任务是必不可少的。 ...

    kitsune-numbers:任意精度算术,根据大小平衡速度和内存

    可能的转换(到和从)是: Stringint long BigIntegerdouble BigDecimal此外,可以直接分配有理值。班级该软件包包含: 用于存储有理数的寄存器类。 它将始终保存存储在其中的确切有理数。 它不会溢出。 它是可变的...

    c/c++ 学习总结 初学者必备

    17、堆栈溢出一般是由什么原因导致的? 答: 没有回收垃圾资源。 18、什么是预编译?何时需要预编译? 答: (1)总是使用不经常改动的大型代码体。 (2)程序由多个模块组成,所有模块都使用一组标准的包含文件和相同...

    java 面试题 总结

    比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。 16、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 ...

    超级有影响力霸气的Java面试题大全文档

    比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。 19、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 ...

    java面试题

    答:运行时异常时(JVM)java虚拟机在运行过程中发生的问题,比如:内存溢出等问题。这类异常没法要求程序员去一一捕获并抛出,一般异常是Java类库或程序员自己写的代码发生的错误,这类异常可以由我们去一一捕获并...

    进销存系统文档作业例子

    比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。 16、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 ...

    C#微软培训资料

    &lt;&lt;page 1&gt;&gt; page begin==================== 目 目目 目 录 录录 录 第一部分 C#语言概述.4 第一章 第一章第一章 第一章 .NET 编 编 ... 比尔....这一天 微软公司正式推出了其下一代...

    《你必须知道的495个C语言问题》

    这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制结构域的对齐方式? 27  2.14 为什么sizeof返回的值大于结构大小的期望值,是不是尾部有填充? 28 2.15 如何确定域在结构中的...

    你必须知道的495个C语言问题

    这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制结构域的对齐方式? 2.14 为什么sizeof返回的值大于结构大小的期望值,是不是尾部有填充? 2.15 如何确定域在结构中的字节偏移...

    C语言FAQ 常见问题列表

    o 3.10 我的编译器在结构中留下了空洞, 这导致空间浪费而且无法与外部数据文件进行 "二进制" 读写。能否关掉填充, 或者控制结构域的对齐方式? o 3.11 为什么 sizeof 返回的值大于结构的期望值, 是不是尾部有填充? ...

    一些C面试题,希望能对大家有帮助

    11. 堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源 12. 什么函数不能声明为虚函数? constructor 13. 冒泡排序算法的时间复杂度是什么? O(n^2) 14. 写出float x 与“零值”比较的if语句。 if(x&gt;0.000001&&x) ...

    你必须知道的495个C语言问题(PDF)

    2.10 我的编译器在结构中留下了空洞, 这导致空间浪费而且无法与外 部数据文件进行”二进制” 读写。能否关掉填充, 或者控制结构域 的对齐方式? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.11...

    JSTL详细标签库介绍

    &lt;BR&gt;3、常见异常实例包括:数组下标越界,算法溢出(超出数值表达范围),除数为零,无效参数、内存溢出异常处理功能:主要处理一些同步异常(除数为0),不宜处理一些异步事件(Disk I/O End、网络信息到达、点击...

    用C编写班级成绩管理系统

    #include "string.h" /*字符串函数*/ #include "conio.h" /*屏幕操作函数*/ #include "mem.h" /*内存操作函数*/ #include "ctype.h" /*字符操作函数*/ #include "alloc.h" /*动态地址分配函数*/ #include "dos....

    HGE_系列教材(1-9)

    颜色的每个分量使用浮点数表示,范围是[0-1],相加操作可能导致溢出,一种处理的方式 就是,如果溢出,则设定值为1。 3. 混合模式: 1)BLEND_COLORADD 表示顶点的颜色与纹理的纹元(texel)颜色相加,这使得纹理变...

    MySQL 5.1中文手冊

    7.5.5. MySQL如何使用内存 7.5.6. MySQL如何使用DNS 7.6. 磁盘事宜 7.6.1. 使用符号链接 8. 客户端和实用工具程序 8.1. 客户端脚本和实用工具概述 8.2. myisampack:生成压缩、只读MyISAM表 8.3. mysql:MySQL命令行...

Global site tag (gtag.js) - Google Analytics