2019独角兽企业重金招聘Python工程师标准>>>

String可以说是最常用的Java类型之一了,但是最近听说JDK6里面String.substring存在内存泄露的bug,伙惊呆!一起来看看到底是啥情况吧。

这个是可以导致Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 的代码:

public class TestGC {private String largeString = new String(new byte[100000]);String getString() {        return this.largeString.substring(0, 2);//在JDK6里会导致out of memory,在JDK7和8不会出现问题//        return new String("ab");//        return this.largeString.substring(0,2) + "";//JDK6下的解决方法,不会出现out of memory//        return new String(this.largeString.substring(0, 2));/JDK6下的解决方法,不会出现out of memory} public static void main(String[] args) {java.util.List<String> list = new java.util.ArrayList<String>();        for (int i = 0; i < 100000; i++) {TestGC gc = new TestGC();list.add(gc.getString());}System.out.println("over" + list.size());}
}

但是用JDK8运行,平安无事。注意,之前看的网上文章又说安装了JDK8,只需要在Eclipse里面选Compiler选项为JDK6就可以了,我实 验是不可以的,自己想想String是JDK里面rt.jar的类,就算是编译为JDK6的代码,运行的时候还是用的JDK8的String啊,所以无法 复现bug才是正常的。要复现,只能下载安装JDK6.

有人认为这个会out of memory是因为TestGC对象里面有很大largeString的对象,但是其实在调用getString方法后,TestGC对象完全可以被回收 的,largeString也可以回收,JVM的自动垃圾回收应该不会有bug吧,不然还得了!将getString方法改为直接返回一个String对 象,就可以看出,不会有问题。

现在来看看为什么JDK6里面,substring会导致错误呢。Ctrl+B(IDEA的查看源码快捷键点进去看下),代码如下

public String substring(int beginIndex, int endIndex) {    if (beginIndex < 0) {        throw new StringIndexOutOfBoundsException(beginIndex);}    if (endIndex > count) {        throw new StringIndexOutOfBoundsException(endIndex);}    if (beginIndex > endIndex) {        throw new StringIndexOutOfBoundsException(endIndex - beginIndex);}    return ((beginIndex == 0) && (endIndex == count)) ? this :        new String(offset + beginIndex, endIndex - beginIndex, value);}

前面几行主要是做范围检查,最主要的是

new String(offset + beginIndex, endIndex - beginIndex, value);
String(int offset, int count, char value[]) {this.value = value;this.offset = offset;this.count = count;
}

可以看到JDK6里的substring复用了原来大String的整个value,即String里存放实际char的数组

/** The value is used for character storage. */private final char value[];

而只是通过修改beginIndex和offset来达到复用value,避免数组copy的麻烦(以及可以提高一点性能),但是问题就是,如果原 String很大,而substring保留的时间比较久,就有可能导致整个很大的value无法回收。JDK6下的修复方法就是,强制生成一个新的 String,避免复用原来String里的value,比如:

return this.largeString.substring(0,2) + "";//JDK6下的解决方法,不会出现out of memory

其实,这恰恰也是JDK8里面的实现方式。上src:

public String substring(int beginIndex, int endIndex) {        if (beginIndex < 0) {            throw new StringIndexOutOfBoundsException(beginIndex);}        if (endIndex > value.length) {            throw new StringIndexOutOfBoundsException(endIndex);}        int subLen = endIndex - beginIndex;        if (subLen < 0) {            throw new StringIndexOutOfBoundsException(subLen);}        return ((beginIndex == 0) && (endIndex == value.length)) ? this: new String(value, beginIndex, subLen);}

跟前面区别不大,再来看

public String(char value[], int offset, int count) {        if (offset < 0) {            throw new StringIndexOutOfBoundsException(offset);}        if (count < 0) {            throw new StringIndexOutOfBoundsException(count);}        // Note: offset or count might be near -1>>>1.if (offset > value.length - count) {            throw new StringIndexOutOfBoundsException(offset + count);}        this.value = Arrays.copyOfRange(value, offset, offset+count);}

可以看到,最后对value做了数组copy。

其实JDK8的修改也是褒贬不一,也有人认为JDK6里面的实现方法更好,效率更高,只要自己注意就可以避免问题的,这就是仁者见仁智者见智的问题了,只是需要知道,JDK6里String的这个小坑就好。

参考文章

  1. http://droidyue.com/blog/2014/12/14/substring-memory-issue-in-java/

  2. http://www.programcreek.com/2013/09/the-substring-method-in-jdk-6-and-jdk-7/

转载于:https://my.oschina.net/magicly007/blog/497227

Java String.substring内存泄露?相关推荐

  1. String.subString内存泄露

    在jdk6中,substring还是指向原来的字符串,可以用 new String(str.substring())避免内存泄露 在jdk7中,没有这个问题

  2. Java中的内存泄露的几种可能

    转载自  Java中的内存泄露的几种可能 Java内存泄漏引起的原因: 内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏. 长生命周 ...

  3. Java String substring()方法示例

    Java String substring() method returns the substring of this string. This method always returns a ne ...

  4. java程序会发生内存泄露吗及内存泄漏场景

    java程序会发生内存泄露的问题吗?请简单说说你的观点 答案:会.Java内存管理是通过垃圾收集器(Garbage Collection,GC)自动管理内存的回收的,java程序员不需要通过调用函数来 ...

  5. java string.substring 参数,Java,String类中的subString()方法,stringsubstring

    Java,String类中的subString()方法,stringsubstring public class TestStringSubString { // main函数 public stat ...

  6. java string占用内存_Java中String到底占用多大的内存空间?

    写在前面 对于Java中的String类占用多大的内存空间这个问题,是最近面试中问的比较多的一个问题.很多小伙伴的回答的都不是很正确,有说不占空间的,有说1个字节的,有说2个字节的,有说3个字节的,有 ...

  7. java 堆外内存泄露排查

    参考:http://blog.itpub.net/70016482/viewspace-2908649/ 一.原理 JDK提供绕过JVM直接在操作系统申请内存的接口,例如通过Unsafe类的alloc ...

  8. Java堆外内存泄露分析

    查看堆内存占用正常,jvm垃圾回收也没有异常.而top出来显示java占用内存是几个G,那么可能想到了是堆外内存泄漏. 需要安装google-perftools工具进行分析 1.先安装g++ 不然编译 ...

  9. java方法区内存泄露_深入理解java虚拟机-第二章:java内存区域与内存泄露异常...

    2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...

最新文章

  1. 周志华教授:AI领域如何做优秀研究并写高水平论文?
  2. Wireshark默认不抓取本地包的解决方式
  3. 6.(Mysql数据管理相关)连接MYSQL,修改密码,增加新用户,数据库相关命令,表操作相关命令,数据相关命令,数据库sql导入和导出,备份数据库,查看不到mysql数据库的解决办法
  4. 【数据结构与算法】之深入解析“串联所有单词的子串”的求解思路与算法示例
  5. 【Flask】jinja2根据url中的字符串,决定是否显示某个div
  6. 三维错切变换矩阵_图像的仿射变换
  7. (转) android UI进阶之用gallery实现可滑动的Tab
  8. java第一章_java 第一章
  9. 大数据的趋势走向如何
  10. 第九次作业(杨辉三角)
  11. angularjs(显示和隐身) 依赖注入
  12. 万能打印之Delphi 2010实现(一)
  13. 基于Matlab的极限学习机(ELM)实现
  14. cp105b linux 驱动,富士施乐 DocuPrint CP105b驱动
  15. python图像风格迁移_Python 超简单实现9种图像风格迁移
  16. 乐高叉车wedo教案_特色课程——乐高wedo动力机械
  17. 立秋养生,老话说“三不吃、三不做、三不睡”,精神饱满健康过秋
  18. ZABBIX 钉钉报警 插件(python)
  19. 【懒人必备神器】教你用Python做一个自动抽奖程序啦~
  20. 什么是流批一体化、区块链

热门文章

  1. ptam tracking
  2. Spring源码分析【1】-Tomcat的初始化
  3. Windows7/10上配置OpenCV3.3.0-Python3.6.2操作步骤
  4. CUDA Samples:Vector Add
  5. 深度学习开源库tiny-dnn的使用(MNIST)
  6. matlab常遇小问题汇总
  7. 【杂】突然有个想法,为了防止公司或其他,监视你的qq或微信,可以做个程序,将信息打乱,分别用qq和微信传输,然后,再还原
  8. 【OpenCV】使用过的函数汇总
  9. 因子分析数据_Excel数据分析案例:用Excel做因子分析
  10. 7-5 二分法求多项式单根 (20分)