Java程序性能优化 - subString()方法的内存泄露

在《Java程序性能优化》3.12小节用一个例子说明在Java 6之前的版本(Java 7中已经解决)使用subString方法可能会带来性能的问题,但是并没有说明为什么会出现性能问题,我从JVM内存模型层面试着分析这个问题。

源代码:

public class Test {
	public static void main(String[] args) {
		List<String> handler = new ArrayList<String>();

		for (int i = 0; i < 1000; i++) {
			HugeStr h = new HugeStr(); // Line 1
			//ImprovedHugeStr h = new ImprovedHugeStr(); // Line 2
			handler.add(h.getSubString(1, 5)); // Line 3
		}
	}

	static class HugeStr {
		private String str = new String(new char[100000]);
		
		public String getSubString(int begin, int end) {
			return str.substring(begin, end);
		}
	}
	
	static class ImprovedHugeStr {
		private String str = new String(new char[100000]);
		
		public String getSubString(int begin, int end) {
			return new String(str.substring(begin, end));
		}
	}
}
 

这是HugeStr一次for循环的简易内存分配示意图。



 说明:

Line 1这一行实际上做了两个操作,在堆中创建了一个新的HugeStr对象,并将h指向堆中新创建的HugeStr对象;而HugeStr对象中又包含一个str属性,同理,此刻str将指向内存中另外String对象,该String对象包含一个value属性指向一个100000长度的数组。

Line 3这一行关注h.getSubString()这个操作,在Java 7以前,这里会新new一个字符串,并将原字符串的value的引用传递给新的字符串,如说上3.12第三幅图展示的样子,这样新的字符串的value属性则指向了Line 1上那个长度很长的字符串。

当循环结束后,h的引用变成无效引用,那么其他相关联的无效引用如图中红色X所示。但是100000长度的数组则不能被确定为无效引用,因为ArrayList.add()方法执行之后,会将Line 3生成的对象放置在该集合中。

对比ImprovedHugeStr



 

说明:ImprovedHugeStr与HugeStr唯一的区别在于在getSubstring()方法的时候新new了一个字符串,而正好是这个new操作,Java将会将原来value的数组复制并生成新的数组,也就使得大数组的引用在经过循环之后变成了无效引用,当垃圾回收器需要进行gc的时候会将其回收。上JDK源码。

public String(String original) {
	int size = original.count;
	char[] originalValue = original.value;
	char[] v;
  	if (originalValue.length > size) {
 	    // The array representing the String is bigger than the new
 	    // String itself.  Perhaps this constructor is being called
 	    // in order to trim the baggage, so make a copy of the array.
            int off = original.offset;
            v = Arrays.copyOfRange(originalValue, off, off+size);
 	} else {
 	    // The array representing the String is the same
 	    // size as the String, so no point in making a copy.
	    v = originalValue;
 	}
	this.offset = 0;
	this.count = size;
	this.value = v;
    }

猜你喜欢

转载自shuaikai-shuai.iteye.com/blog/2413290
今日推荐