java.lang.String的substring、split会导致内存溢出

戚薇 Java经验 发布时间:2023-06-26 16:48:03 阅读数:15299 1
下文笔者讲述String的substring方法导致OutOfMemoryError的处理方法分享 例:异常测试示例
public class TestClass {   
  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++) {   
      TestClass testClass = new TestClass();   
      subStrings.add(testClass.getSubString());   
    }   
  }   
}

//运行以上程序,会报以下异常
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

出现OutOfMemoryError异常的原因

    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);
    }
该方法最后一行,调用了String的一个私有的构造方法,如下:
    // Package private constructor which shares value array for speed.
    String(int offset, int count, char value[]) {
	this.value = value;
	this.offset = offset;
	this.count = count;
    }
该方法为
避免内存拷贝,提高性能,
并没有重新创建char数组,
而直接复用原String对象的char[]
通过改变偏移量和长度来标识不同的字符串内容

substring出的来String小对象
 仍然会指向原String大对象的char[]
 所以就导致OutOfMemoryError问题
getSubString方法变更
public String getSubString() {
	return new String(this.large.substring(0,2)); 
}
将substring的结果
重新new一个String出来
再运行该程序,则没有出现OutOfMemoryError的问题

除substring方法外
String的split方法
也存在同样的问题
split源码如下:
    public String[] split(String regex, int limit) {
	return Pattern.compile(regex).split(this, limit);
    }


//String的split方法通过Pattern的split方法来实现
//Pattern的split方法:

public String[] split(CharSequence input, int limit) {
        int index = 0;
        boolean matchLimited = limit > 0;
        ArrayList<String> matchList = new ArrayList<String>();
        Matcher m = matcher(input);
 
        // Add segments before each match found
        while(m.find()) {
            if (!matchLimited || matchList.size() < limit - 1) {
                String match = input.subSequence(index, m.start()).toString();
                matchList.add(match);
                index = m.end();
            } else if (matchList.size() == limit - 1) { // last one
                String match = input.subSequence(index,
                                                 input.length()).toString();
                matchList.add(match);
                index = m.end();
            }
        }
 
        // If no match was found, return this
        if (index == 0)
            return new String[] {input.toString()};
 
        // Add remaining segment
        if (!matchLimited || matchList.size() < limit)
            matchList.add(input.subSequence(index, input.length()).toString());
 
        // Construct result
        int resultSize = matchList.size();
        if (limit == 0)
            while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
                resultSize--;
        String[] result = new String[resultSize];
        return matchList.subList(0, resultSize).toArray(result);
    }
方法中
  Stirng match = input.subSequence(intdex, m.start()).toString();
调用String类的subSequence方法
  该方法源码如下:
public CharSequence subSequence(int beginIndex, int endIndex) {
	return this.substring(beginIndex, endIndex);
}
从源码上
  我们可以看出
    最终调用的是String类的substring方法
    因此存在同样的问题
    split出来的小对象
     直接使用原String对象的char[]。

StringBuilder和StringBuffer的substring方法
 //则不存在这样的问题
 //其源码如下

    public String substring(int start, int end) {
	if (start < 0)
	    throw new StringIndexOutOfBoundsException(start);
	if (end > count)
	    throw new StringIndexOutOfBoundsException(end);
	if (start > end)
	    throw new StringIndexOutOfBoundsException(end - start);
        return new String(value, start, end - start);
    }

最后调用String类的public构造方法,源码如下:
    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.offset = 0;
        this.count = count;
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }
该方法不是直接使用原String对象的char[]
而重新进行了内存拷贝,所以不会出现内存溢出

版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaJingYan/202306/16877693176901.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者