提交 8d027635 编写于 作者: D dholmes

8013395: StringBuffer.toString performance regression impacting embedded benchmarks

Summary: cache a copy of the char[] to use with toString() and clear it when ever the sb content is modified
Reviewed-by: alanb, plevart, mduigou, forax
上级 3c1270f5
/* /*
* Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
package java.lang; package java.lang;
import java.util.Arrays;
/** /**
* A thread-safe, mutable sequence of characters. * A thread-safe, mutable sequence of characters.
...@@ -98,6 +99,12 @@ package java.lang; ...@@ -98,6 +99,12 @@ package java.lang;
implements java.io.Serializable, CharSequence implements java.io.Serializable, CharSequence
{ {
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */ /** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L; static final long serialVersionUID = 3388685877147921107L;
...@@ -183,6 +190,7 @@ package java.lang; ...@@ -183,6 +190,7 @@ package java.lang;
*/ */
@Override @Override
public synchronized void setLength(int newLength) { public synchronized void setLength(int newLength) {
toStringCache = null;
super.setLength(newLength); super.setLength(newLength);
} }
...@@ -247,17 +255,20 @@ package java.lang; ...@@ -247,17 +255,20 @@ package java.lang;
public synchronized void setCharAt(int index, char ch) { public synchronized void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count)) if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index); throw new StringIndexOutOfBoundsException(index);
toStringCache = null;
value[index] = ch; value[index] = ch;
} }
@Override @Override
public synchronized StringBuffer append(Object obj) { public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj)); super.append(String.valueOf(obj));
return this; return this;
} }
@Override @Override
public synchronized StringBuffer append(String str) { public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str); super.append(str);
return this; return this;
} }
...@@ -287,6 +298,7 @@ package java.lang; ...@@ -287,6 +298,7 @@ package java.lang;
* @since 1.4 * @since 1.4
*/ */
public synchronized StringBuffer append(StringBuffer sb) { public synchronized StringBuffer append(StringBuffer sb) {
toStringCache = null;
super.append(sb); super.append(sb);
return this; return this;
} }
...@@ -296,6 +308,7 @@ package java.lang; ...@@ -296,6 +308,7 @@ package java.lang;
*/ */
@Override @Override
synchronized StringBuffer append(AbstractStringBuilder asb) { synchronized StringBuffer append(AbstractStringBuilder asb) {
toStringCache = null;
super.append(asb); super.append(asb);
return this; return this;
} }
...@@ -325,6 +338,7 @@ package java.lang; ...@@ -325,6 +338,7 @@ package java.lang;
public StringBuffer append(CharSequence s) { public StringBuffer append(CharSequence s) {
// Note, synchronization achieved via invocations of other StringBuffer methods after // Note, synchronization achieved via invocations of other StringBuffer methods after
// narrowing of s to specific type // narrowing of s to specific type
// Ditto for toStringCache clearing
super.append(s); super.append(s);
return this; return this;
} }
...@@ -336,12 +350,14 @@ package java.lang; ...@@ -336,12 +350,14 @@ package java.lang;
@Override @Override
public synchronized StringBuffer append(CharSequence s, int start, int end) public synchronized StringBuffer append(CharSequence s, int start, int end)
{ {
toStringCache = null;
super.append(s, start, end); super.append(s, start, end);
return this; return this;
} }
@Override @Override
public synchronized StringBuffer append(char[] str) { public synchronized StringBuffer append(char[] str) {
toStringCache = null;
super.append(str); super.append(str);
return this; return this;
} }
...@@ -351,24 +367,28 @@ package java.lang; ...@@ -351,24 +367,28 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer append(char[] str, int offset, int len) { public synchronized StringBuffer append(char[] str, int offset, int len) {
toStringCache = null;
super.append(str, offset, len); super.append(str, offset, len);
return this; return this;
} }
@Override @Override
public synchronized StringBuffer append(boolean b) { public synchronized StringBuffer append(boolean b) {
toStringCache = null;
super.append(b); super.append(b);
return this; return this;
} }
@Override @Override
public synchronized StringBuffer append(char c) { public synchronized StringBuffer append(char c) {
toStringCache = null;
super.append(c); super.append(c);
return this; return this;
} }
@Override @Override
public synchronized StringBuffer append(int i) { public synchronized StringBuffer append(int i) {
toStringCache = null;
super.append(i); super.append(i);
return this; return this;
} }
...@@ -378,24 +398,28 @@ package java.lang; ...@@ -378,24 +398,28 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer appendCodePoint(int codePoint) { public synchronized StringBuffer appendCodePoint(int codePoint) {
toStringCache = null;
super.appendCodePoint(codePoint); super.appendCodePoint(codePoint);
return this; return this;
} }
@Override @Override
public synchronized StringBuffer append(long lng) { public synchronized StringBuffer append(long lng) {
toStringCache = null;
super.append(lng); super.append(lng);
return this; return this;
} }
@Override @Override
public synchronized StringBuffer append(float f) { public synchronized StringBuffer append(float f) {
toStringCache = null;
super.append(f); super.append(f);
return this; return this;
} }
@Override @Override
public synchronized StringBuffer append(double d) { public synchronized StringBuffer append(double d) {
toStringCache = null;
super.append(d); super.append(d);
return this; return this;
} }
...@@ -406,6 +430,7 @@ package java.lang; ...@@ -406,6 +430,7 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer delete(int start, int end) { public synchronized StringBuffer delete(int start, int end) {
toStringCache = null;
super.delete(start, end); super.delete(start, end);
return this; return this;
} }
...@@ -416,6 +441,7 @@ package java.lang; ...@@ -416,6 +441,7 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer deleteCharAt(int index) { public synchronized StringBuffer deleteCharAt(int index) {
toStringCache = null;
super.deleteCharAt(index); super.deleteCharAt(index);
return this; return this;
} }
...@@ -426,6 +452,7 @@ package java.lang; ...@@ -426,6 +452,7 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer replace(int start, int end, String str) { public synchronized StringBuffer replace(int start, int end, String str) {
toStringCache = null;
super.replace(start, end, str); super.replace(start, end, str);
return this; return this;
} }
...@@ -465,6 +492,7 @@ package java.lang; ...@@ -465,6 +492,7 @@ package java.lang;
public synchronized StringBuffer insert(int index, char[] str, int offset, public synchronized StringBuffer insert(int index, char[] str, int offset,
int len) int len)
{ {
toStringCache = null;
super.insert(index, str, offset, len); super.insert(index, str, offset, len);
return this; return this;
} }
...@@ -474,6 +502,7 @@ package java.lang; ...@@ -474,6 +502,7 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer insert(int offset, Object obj) { public synchronized StringBuffer insert(int offset, Object obj) {
toStringCache = null;
super.insert(offset, String.valueOf(obj)); super.insert(offset, String.valueOf(obj));
return this; return this;
} }
...@@ -483,6 +512,7 @@ package java.lang; ...@@ -483,6 +512,7 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer insert(int offset, String str) { public synchronized StringBuffer insert(int offset, String str) {
toStringCache = null;
super.insert(offset, str); super.insert(offset, str);
return this; return this;
} }
...@@ -492,6 +522,7 @@ package java.lang; ...@@ -492,6 +522,7 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer insert(int offset, char[] str) { public synchronized StringBuffer insert(int offset, char[] str) {
toStringCache = null;
super.insert(offset, str); super.insert(offset, str);
return this; return this;
} }
...@@ -504,6 +535,7 @@ package java.lang; ...@@ -504,6 +535,7 @@ package java.lang;
public StringBuffer insert(int dstOffset, CharSequence s) { public StringBuffer insert(int dstOffset, CharSequence s) {
// Note, synchronization achieved via invocations of other StringBuffer methods // Note, synchronization achieved via invocations of other StringBuffer methods
// after narrowing of s to specific type // after narrowing of s to specific type
// Ditto for toStringCache clearing
super.insert(dstOffset, s); super.insert(dstOffset, s);
return this; return this;
} }
...@@ -516,6 +548,7 @@ package java.lang; ...@@ -516,6 +548,7 @@ package java.lang;
public synchronized StringBuffer insert(int dstOffset, CharSequence s, public synchronized StringBuffer insert(int dstOffset, CharSequence s,
int start, int end) int start, int end)
{ {
toStringCache = null;
super.insert(dstOffset, s, start, end); super.insert(dstOffset, s, start, end);
return this; return this;
} }
...@@ -527,6 +560,7 @@ package java.lang; ...@@ -527,6 +560,7 @@ package java.lang;
public StringBuffer insert(int offset, boolean b) { public StringBuffer insert(int offset, boolean b) {
// Note, synchronization achieved via invocation of StringBuffer insert(int, String) // Note, synchronization achieved via invocation of StringBuffer insert(int, String)
// after conversion of b to String by super class method // after conversion of b to String by super class method
// Ditto for toStringCache clearing
super.insert(offset, b); super.insert(offset, b);
return this; return this;
} }
...@@ -536,6 +570,7 @@ package java.lang; ...@@ -536,6 +570,7 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer insert(int offset, char c) { public synchronized StringBuffer insert(int offset, char c) {
toStringCache = null;
super.insert(offset, c); super.insert(offset, c);
return this; return this;
} }
...@@ -547,6 +582,7 @@ package java.lang; ...@@ -547,6 +582,7 @@ package java.lang;
public StringBuffer insert(int offset, int i) { public StringBuffer insert(int offset, int i) {
// Note, synchronization achieved via invocation of StringBuffer insert(int, String) // Note, synchronization achieved via invocation of StringBuffer insert(int, String)
// after conversion of i to String by super class method // after conversion of i to String by super class method
// Ditto for toStringCache clearing
super.insert(offset, i); super.insert(offset, i);
return this; return this;
} }
...@@ -558,6 +594,7 @@ package java.lang; ...@@ -558,6 +594,7 @@ package java.lang;
public StringBuffer insert(int offset, long l) { public StringBuffer insert(int offset, long l) {
// Note, synchronization achieved via invocation of StringBuffer insert(int, String) // Note, synchronization achieved via invocation of StringBuffer insert(int, String)
// after conversion of l to String by super class method // after conversion of l to String by super class method
// Ditto for toStringCache clearing
super.insert(offset, l); super.insert(offset, l);
return this; return this;
} }
...@@ -569,6 +606,7 @@ package java.lang; ...@@ -569,6 +606,7 @@ package java.lang;
public StringBuffer insert(int offset, float f) { public StringBuffer insert(int offset, float f) {
// Note, synchronization achieved via invocation of StringBuffer insert(int, String) // Note, synchronization achieved via invocation of StringBuffer insert(int, String)
// after conversion of f to String by super class method // after conversion of f to String by super class method
// Ditto for toStringCache clearing
super.insert(offset, f); super.insert(offset, f);
return this; return this;
} }
...@@ -580,6 +618,7 @@ package java.lang; ...@@ -580,6 +618,7 @@ package java.lang;
public StringBuffer insert(int offset, double d) { public StringBuffer insert(int offset, double d) {
// Note, synchronization achieved via invocation of StringBuffer insert(int, String) // Note, synchronization achieved via invocation of StringBuffer insert(int, String)
// after conversion of d to String by super class method // after conversion of d to String by super class method
// Ditto for toStringCache clearing
super.insert(offset, d); super.insert(offset, d);
return this; return this;
} }
...@@ -623,13 +662,17 @@ package java.lang; ...@@ -623,13 +662,17 @@ package java.lang;
*/ */
@Override @Override
public synchronized StringBuffer reverse() { public synchronized StringBuffer reverse() {
toStringCache = null;
super.reverse(); super.reverse();
return this; return this;
} }
@Override @Override
public synchronized String toString() { public synchronized String toString() {
return new String(value, 0, count); if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
} }
/** /**
......
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8013395
* @summary Test StringBuffer.toString caching
*/
public class ToStringCache {
// we can't test that we actually use a cached value (the benchmarks
// verify that) but we have to test that the cache is cleared when
// expected
public static void main(String[] args) throws Exception {
String original = "The original String";
StringBuffer sb = new StringBuffer(original);
String a = sb.toString();
checkEqual(a, original);
String b = sb.toString();
checkEqual(a, b);
// mutating methods
sb.setLength(12);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.setCharAt(0, 'X');
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(new Character('X'));
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append("More text");
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(sb);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(new StringBuilder("Build"));
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(new StringBuilder("Build2"), 0, 1);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(new char[] { 'a', 'b' });
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(true);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append('c');
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(23);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.appendCodePoint(Character.codePointAt(new char[] { 'X'}, 0));
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(1L);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(1.0f);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.append(1.0d);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.delete(0, 5);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.deleteCharAt(0);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.replace(0,2, "123");
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, new char[] { 'a', 'b', 'c'}, 0, 3);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, new Object());
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, "abc");
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, new char[] { 'a', 'b', 'c' });
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, new StringBuilder("Build"));
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, new StringBuilder("Build"), 0, 1);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, false);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, 'X');
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, 1);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, 1L);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, 1.0f);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.insert(0, 1.0d);
b = sb.toString();
checkUnequal(a, b);
a = b;
sb.reverse();
b = sb.toString();
checkUnequal(a, b);
// non-mutating methods
// Reset to known value
sb = new StringBuffer(original);
a = sb.toString();
b = sb.toString();
checkEqual(a, b);
int l = sb.length();
b = sb.toString();
checkEqual(a, b);
int cap = sb.capacity();
b = sb.toString();
checkEqual(a, b);
sb.ensureCapacity(100);
b = sb.toString();
checkEqual(a, b);
sb.trimToSize();
b = sb.toString();
checkEqual(a, b);
char c = sb.charAt(1);
b = sb.toString();
checkEqual(a, b);
int cp = sb.codePointAt(1);
b = sb.toString();
checkEqual(a, b);
cp = sb.codePointBefore(2);
b = sb.toString();
checkEqual(a, b);
int count = sb.codePointCount(0,1);
b = sb.toString();
checkEqual(a, b);
count = sb.offsetByCodePoints(0, 1);
b = sb.toString();
checkEqual(a, b);
sb.getChars(0, 1, new char[2], 0);
b = sb.toString();
checkEqual(a, b);
String sub = sb.substring(0);
b = sb.toString();
checkEqual(a, b);
CharSequence cs = sb.subSequence(0,1);
b = sb.toString();
checkEqual(a, b);
sub = sb.substring(0, 3);
b = sb.toString();
checkEqual(a, b);
int index = sb.indexOf("rig");
b = sb.toString();
checkEqual(a, b);
index = sb.indexOf("rig", 2);
b = sb.toString();
checkEqual(a, b);
index = sb.lastIndexOf("rig");
b = sb.toString();
checkEqual(a, b);
index = sb.lastIndexOf("rig", 3);
b = sb.toString();
checkEqual(a, b);
}
private static void checkEqual(String s1, String s2) {
if (!s1.equals(s2))
throw new RuntimeException("Unmatched strings: s1 = "
+ s1 + " s2 = " + s2);
}
private static void checkUnequal(String s1, String s2) {
if (s1.equals(s2))
throw new RuntimeException("Unexpected matched strings: " + s1);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册