标签:
测试环境
eclipse + jdk1.6.0_25
public class SubMain {
private String strs =
new String(
new byte[100000]);
String getString() {
return this.strs.substring(0, 2);
}
public static void main(String[] args) {
List<String> list =
new ArrayList<String>();
for (
int i = 0; i < 1000000; i++) {
SubMain sub =
new SubMain();
list.add(sub.getString());
}
}
}
运行不到一分钟后
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:133)
at java.lang.StringCoding.decode(StringCoding.java:173)
at java.lang.StringCoding.decode(StringCoding.java:185)
at java.lang.String.<init>(String.java:570)
at java.lang.String.<init>(String.java:593)
at com.jd.o2o.substring.SubMain.<init>(SubMain.java:8)
at com.jd.o2o.substring.SubMain.main(SubMain.java:18)
好吧,内存溢出了
查看jdk6 substring源码实现,看看为什么?我们集合中也只存储了两个字符串,不至于说会内存溢出,即使我们全局变量占用空间大的话,在我们不使用该对象时,该对象应该会被清理掉,这样也不应该导致内存溢出
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(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
好吧,真相是jdk6 substring方法的实现采用的是原value保留,只是改变偏移量和count;一切都已经明朗了,集合中存储数据一直都的是全局变量的大小,所以当创建对象很多的情况下,一直累积,因为一直有引用,所以gc不掉 最终导致OOM了
第一种解决方案:修改getString方法实现,将截取后的值采用new出来的对象进行存储
String getString() {
return new String(
this.strs.substring(0, 2));
}
第二种解决方案:jdk切到jdk1.7.0_79
为什么切到1.7之后就可以了呢?因为jdk底层substring方法实现改了,我们还是继续看jdk7的substring方法实现
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);
}
好吧!还真改了,采用Arrays.copyOfRange了
public static char[] copyOfRange(
char[] original,
int from,
int to) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
char[] copy =
new char[newLength];
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
最终是采用的System.arraycopy意思是将截取后的字符串复制一份出来
这样就解决了jdk6内存溢出的问题了
jdk6 substring 内存泄露问题解析
标签:
原文地址:http://www.cnblogs.com/hxy520/p/5450893.html