mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-20 15:25:27 +00:00
8013395: StringBuffer.toString performance regression impacting embedded benchmarks
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
This commit is contained in:
parent
bedabba81c
commit
4e4591b4da
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,6 +25,7 @@
|
||||
|
||||
package java.lang;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A thread-safe, mutable sequence of characters.
|
||||
@ -98,6 +99,12 @@ package java.lang;
|
||||
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 */
|
||||
static final long serialVersionUID = 3388685877147921107L;
|
||||
|
||||
@ -183,6 +190,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized void setLength(int newLength) {
|
||||
toStringCache = null;
|
||||
super.setLength(newLength);
|
||||
}
|
||||
|
||||
@ -247,17 +255,20 @@ package java.lang;
|
||||
public synchronized void setCharAt(int index, char ch) {
|
||||
if ((index < 0) || (index >= count))
|
||||
throw new StringIndexOutOfBoundsException(index);
|
||||
toStringCache = null;
|
||||
value[index] = ch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(Object obj) {
|
||||
toStringCache = null;
|
||||
super.append(String.valueOf(obj));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(String str) {
|
||||
toStringCache = null;
|
||||
super.append(str);
|
||||
return this;
|
||||
}
|
||||
@ -287,6 +298,7 @@ package java.lang;
|
||||
* @since 1.4
|
||||
*/
|
||||
public synchronized StringBuffer append(StringBuffer sb) {
|
||||
toStringCache = null;
|
||||
super.append(sb);
|
||||
return this;
|
||||
}
|
||||
@ -296,6 +308,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
synchronized StringBuffer append(AbstractStringBuilder asb) {
|
||||
toStringCache = null;
|
||||
super.append(asb);
|
||||
return this;
|
||||
}
|
||||
@ -325,6 +338,7 @@ package java.lang;
|
||||
public StringBuffer append(CharSequence s) {
|
||||
// Note, synchronization achieved via invocations of other StringBuffer methods after
|
||||
// narrowing of s to specific type
|
||||
// Ditto for toStringCache clearing
|
||||
super.append(s);
|
||||
return this;
|
||||
}
|
||||
@ -336,12 +350,14 @@ package java.lang;
|
||||
@Override
|
||||
public synchronized StringBuffer append(CharSequence s, int start, int end)
|
||||
{
|
||||
toStringCache = null;
|
||||
super.append(s, start, end);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(char[] str) {
|
||||
toStringCache = null;
|
||||
super.append(str);
|
||||
return this;
|
||||
}
|
||||
@ -351,24 +367,28 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer append(char[] str, int offset, int len) {
|
||||
toStringCache = null;
|
||||
super.append(str, offset, len);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(boolean b) {
|
||||
toStringCache = null;
|
||||
super.append(b);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(char c) {
|
||||
toStringCache = null;
|
||||
super.append(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(int i) {
|
||||
toStringCache = null;
|
||||
super.append(i);
|
||||
return this;
|
||||
}
|
||||
@ -378,24 +398,28 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer appendCodePoint(int codePoint) {
|
||||
toStringCache = null;
|
||||
super.appendCodePoint(codePoint);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(long lng) {
|
||||
toStringCache = null;
|
||||
super.append(lng);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(float f) {
|
||||
toStringCache = null;
|
||||
super.append(f);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized StringBuffer append(double d) {
|
||||
toStringCache = null;
|
||||
super.append(d);
|
||||
return this;
|
||||
}
|
||||
@ -406,6 +430,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer delete(int start, int end) {
|
||||
toStringCache = null;
|
||||
super.delete(start, end);
|
||||
return this;
|
||||
}
|
||||
@ -416,6 +441,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer deleteCharAt(int index) {
|
||||
toStringCache = null;
|
||||
super.deleteCharAt(index);
|
||||
return this;
|
||||
}
|
||||
@ -426,6 +452,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer replace(int start, int end, String str) {
|
||||
toStringCache = null;
|
||||
super.replace(start, end, str);
|
||||
return this;
|
||||
}
|
||||
@ -465,6 +492,7 @@ package java.lang;
|
||||
public synchronized StringBuffer insert(int index, char[] str, int offset,
|
||||
int len)
|
||||
{
|
||||
toStringCache = null;
|
||||
super.insert(index, str, offset, len);
|
||||
return this;
|
||||
}
|
||||
@ -474,6 +502,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer insert(int offset, Object obj) {
|
||||
toStringCache = null;
|
||||
super.insert(offset, String.valueOf(obj));
|
||||
return this;
|
||||
}
|
||||
@ -483,6 +512,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer insert(int offset, String str) {
|
||||
toStringCache = null;
|
||||
super.insert(offset, str);
|
||||
return this;
|
||||
}
|
||||
@ -492,6 +522,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer insert(int offset, char[] str) {
|
||||
toStringCache = null;
|
||||
super.insert(offset, str);
|
||||
return this;
|
||||
}
|
||||
@ -504,6 +535,7 @@ package java.lang;
|
||||
public StringBuffer insert(int dstOffset, CharSequence s) {
|
||||
// Note, synchronization achieved via invocations of other StringBuffer methods
|
||||
// after narrowing of s to specific type
|
||||
// Ditto for toStringCache clearing
|
||||
super.insert(dstOffset, s);
|
||||
return this;
|
||||
}
|
||||
@ -516,6 +548,7 @@ package java.lang;
|
||||
public synchronized StringBuffer insert(int dstOffset, CharSequence s,
|
||||
int start, int end)
|
||||
{
|
||||
toStringCache = null;
|
||||
super.insert(dstOffset, s, start, end);
|
||||
return this;
|
||||
}
|
||||
@ -527,6 +560,7 @@ package java.lang;
|
||||
public StringBuffer insert(int offset, boolean b) {
|
||||
// Note, synchronization achieved via invocation of StringBuffer insert(int, String)
|
||||
// after conversion of b to String by super class method
|
||||
// Ditto for toStringCache clearing
|
||||
super.insert(offset, b);
|
||||
return this;
|
||||
}
|
||||
@ -536,6 +570,7 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer insert(int offset, char c) {
|
||||
toStringCache = null;
|
||||
super.insert(offset, c);
|
||||
return this;
|
||||
}
|
||||
@ -547,6 +582,7 @@ package java.lang;
|
||||
public StringBuffer insert(int offset, int i) {
|
||||
// Note, synchronization achieved via invocation of StringBuffer insert(int, String)
|
||||
// after conversion of i to String by super class method
|
||||
// Ditto for toStringCache clearing
|
||||
super.insert(offset, i);
|
||||
return this;
|
||||
}
|
||||
@ -558,6 +594,7 @@ package java.lang;
|
||||
public StringBuffer insert(int offset, long l) {
|
||||
// Note, synchronization achieved via invocation of StringBuffer insert(int, String)
|
||||
// after conversion of l to String by super class method
|
||||
// Ditto for toStringCache clearing
|
||||
super.insert(offset, l);
|
||||
return this;
|
||||
}
|
||||
@ -569,6 +606,7 @@ package java.lang;
|
||||
public StringBuffer insert(int offset, float f) {
|
||||
// Note, synchronization achieved via invocation of StringBuffer insert(int, String)
|
||||
// after conversion of f to String by super class method
|
||||
// Ditto for toStringCache clearing
|
||||
super.insert(offset, f);
|
||||
return this;
|
||||
}
|
||||
@ -580,6 +618,7 @@ package java.lang;
|
||||
public StringBuffer insert(int offset, double d) {
|
||||
// Note, synchronization achieved via invocation of StringBuffer insert(int, String)
|
||||
// after conversion of d to String by super class method
|
||||
// Ditto for toStringCache clearing
|
||||
super.insert(offset, d);
|
||||
return this;
|
||||
}
|
||||
@ -623,13 +662,17 @@ package java.lang;
|
||||
*/
|
||||
@Override
|
||||
public synchronized StringBuffer reverse() {
|
||||
toStringCache = null;
|
||||
super.reverse();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return new String(value, 0, count);
|
||||
if (toStringCache == null) {
|
||||
toStringCache = Arrays.copyOfRange(value, 0, count);
|
||||
}
|
||||
return new String(toStringCache, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
289
jdk/test/java/lang/StringBuffer/ToStringCache.java
Normal file
289
jdk/test/java/lang/StringBuffer/ToStringCache.java
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user