Work around JDK7 String#substring performance regression

String#substring has become significantly slower as of JDK 1.7.0_06 [1],
such that there are performance degradations by a factor of 100-1000 in
ResourceDatabasePopulator, especially for large SQL files.

This commit works around this problem by minimizing the substring scope
to the least amount possible to prevent unnecessary internal copying of
strings (which seems to cause the issue).

[1]: http://mail.openjdk.java.net/pipermail/core-libs-dev/2012-May/010257.html

Issue: SPR-9781
This commit is contained in:
Oliver Gierke
2012-09-11 12:51:27 +02:00
committed by Chris Beams
parent dfe05305e2
commit 3fb3b7d67a
3 changed files with 2057 additions and 5 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@ import org.springframework.util.StringUtils;
* @author Dave Syer
* @author Juergen Hoeller
* @author Chris Beams
* @author Oliver Gierke
* @since 3.0
*/
public class ResourceDatabasePopulator implements DatabasePopulator {
@@ -265,7 +266,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
if (content[i] == '\'') {
inLiteral = !inLiteral;
}
if (!inLiteral && script.substring(i).startsWith(delim)) {
if (!inLiteral && startsWithDelimiter(script, i, delim)) {
return true;
}
}
@@ -273,8 +274,29 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
}
/**
* Split an SQL script into separate statements delimited with the provided delimiter character.
* Each individual statement will be added to the provided <code>List</code>.
* Return whether the substring of a given source {@link String} starting at the
* given index starts with the given delimiter.
*
* @param source the source {@link String} to inspect
* @param startIndex the index to look for the delimiter
* @param delim the delimiter to look for
*/
private boolean startsWithDelimiter(String source, int startIndex, String delim) {
int endIndex = startIndex + delim.length();
if (source.length() < endIndex) {
// String is too short to contain the delimiter
return false;
}
return source.substring(startIndex, endIndex).equals(delim);
}
/**
* Split an SQL script into separate statements delimited with the provided delimiter
* character. Each individual statement will be added to the provided {@code List}.
*
* @param script the SQL script
* @param delim character delimiting each statement (typically a ';' character)
* @param statements the List that will contain the individual statements
@@ -301,7 +323,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
inLiteral = !inLiteral;
}
if (!inLiteral) {
if (script.substring(i).startsWith(delim)) {
if (startsWithDelimiter(script, i, delim)) {
if (sb.length() > 0) {
statements.add(sb.toString());
sb = new StringBuilder();