Никогда не задавались вопросом насколько собственно отличается производительность строковых классов Java?
В данном топике я попытался сравнить производительность java.lang классов String, StringBuilder и StringBuffer.
Не секрет, что в Java существуют три основных класса для работы со строками. Основной класс, который мы чаще всего используем в программах, это String. Особенностью данного класса является то, что он создает неизменяемые строки. Т.е. какой строкой символов мы объект при создании инициализировали, такой она и останется. Поэтому конструкция:
Создаст новый объект содержащий строку «МашаСаша» а исходные объекты будут уничтожены сборщиком мусора.
Если операций конкатенации над одним и тем же строковым объектом производится много, это приводит к интенсивному процессу порождения новых объектов и добавляет работы сборщику мусора.
Если нам нужны изменяемые строки, разработчики нам предлагают другие классы. Первый, который был в Java изначально — StringBuffer и более новый StringBuilder (появился начиная с версии 1.5). Как пишут в документации, StringBuffer — безопасно применять в многопоточных приложениях, но второй более эффективен.
Ну более эффективен, менее эффективен, этот конечно хорошо, но хочется цифр. Ведь иной программист может подумать, а стоит ли использовать «неудобные» StringBuffer/StringBuilder вместо такого замечательного String ради экономии пары милисекунд? Другие скажут, что не бывает таких ситуаций, когда нужно выполнить ну, скажем, сто тысяч конкатенаций… Но любопытно же, велика ли разница?
Читаем строки из текстового файла «onegin.txt» (призовем на помощь классика). Файл имеет размер 297463 байта, utf-8, 27195 слов. Сложим все слова файла в одну строку с использованием всех трех классов и сравним производительность. Чтоб было интереснее, проведем тест на разных JVM и в двух OS. Linux (я использую LinuxMint 9 это такой разукрашенный Ubuntu, если кто не знает) ну и Win XP Pro SP3. Обе операционки 32-битные, ибо пишу я с нетбука с Atom N280. Но мы же не рекорды ставим, нам важна тенденция.
Собственно сама программа, проще некуда:
Варианты для StringBuffer и StringBuilder аналогичны, и отображены в комментариях.
Замеры времени работы каждого варианта для каждой виртуальной машины производились 5 раз и вычислялось среднее значение.
Если нам нужны модифицируемые строки, используем StringBuffer (в многопоточных приложениях) и StringBuilder в остальных. Разница весьма ощутима.
Понятно почему JVM от Open JDK немножко отстает, что-то там Sun или уже Oracle подкрутил. Не совсем понятно, почему такая большая разница в работе с объектами класса String под Linux и WinXP. Толи Win не дает JVM работать так же эффективно как Linux, то ли это особенности реализации JVM… Тут нужно копать дальше.
Для справки:
JRockit — это виртуальная машина от Oracle.
HotSpot — это машина изначально разработанная Sun, теперь само собой куплена Oracle.
Open JDK — Open-source JDK основаная на исходниках в свое время открытых Sun-ом.
В данном топике я попытался сравнить производительность java.lang классов String, StringBuilder и StringBuffer.
Очевидные факты
Не секрет, что в Java существуют три основных класса для работы со строками. Основной класс, который мы чаще всего используем в программах, это String. Особенностью данного класса является то, что он создает неизменяемые строки. Т.е. какой строкой символов мы объект при создании инициализировали, такой она и останется. Поэтому конструкция:
String st = "Маша";
st += "Саша";
Создаст новый объект содержащий строку «МашаСаша» а исходные объекты будут уничтожены сборщиком мусора.
Если операций конкатенации над одним и тем же строковым объектом производится много, это приводит к интенсивному процессу порождения новых объектов и добавляет работы сборщику мусора.
Если нам нужны изменяемые строки, разработчики нам предлагают другие классы. Первый, который был в Java изначально — StringBuffer и более новый StringBuilder (появился начиная с версии 1.5). Как пишут в документации, StringBuffer — безопасно применять в многопоточных приложениях, но второй более эффективен.
Чего хочется
Ну более эффективен, менее эффективен, этот конечно хорошо, но хочется цифр. Ведь иной программист может подумать, а стоит ли использовать «неудобные» StringBuffer/StringBuilder вместо такого замечательного String ради экономии пары милисекунд? Другие скажут, что не бывает таких ситуаций, когда нужно выполнить ну, скажем, сто тысяч конкатенаций… Но любопытно же, велика ли разница?
Тест
Читаем строки из текстового файла «onegin.txt» (призовем на помощь классика). Файл имеет размер 297463 байта, utf-8, 27195 слов. Сложим все слова файла в одну строку с использованием всех трех классов и сравним производительность. Чтоб было интереснее, проведем тест на разных JVM и в двух OS. Linux (я использую LinuxMint 9 это такой разукрашенный Ubuntu, если кто не знает) ну и Win XP Pro SP3. Обе операционки 32-битные, ибо пишу я с нетбука с Atom N280. Но мы же не рекорды ставим, нам важна тенденция.
Собственно сама программа, проще некуда:
package stringtest1;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws FileNotFoundException, IOException {
BufferedReader reader = new BufferedReader(new FileReader("onegin.txt"));
StringBuilder sb = new StringBuilder();
String line = null;
while ( (line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
String[] words = sb.toString().split("\\s+");
System.out.println("Total words:" + words.length);
waitEnter();
long ts = System.nanoTime();
String buff = "";
//2 StringBuffer buff = new StringBuffer();
//3 StringBuilder buff = new StringBuilder();
for (String word : words) {
buff += word + " ";
//2&3 buff.append(word).append(" ");
}
long te =System.nanoTime();
System.out.println("Complete, lenght:" + buff.length() + " elapsed time:" + (te - ts)/1e6 + "ms");
}
private static void waitEnter() {
Scanner scan = new Scanner(System.in);
System.out.print("Press Enter key.");
scan.nextLine();
}
}
* This source code was highlighted with Source Code Highlighter.
Варианты для StringBuffer и StringBuilder аналогичны, и отображены в комментариях.
Замеры времени работы каждого варианта для каждой виртуальной машины производились 5 раз и вычислялось среднее значение.
Результаты
Linux
Класс | Open JDK 1.6.0_18 | HotSpot 1.6.0_20 | JRockit 4.0.1 |
---|---|---|---|
String | 27390ms | 26850ms | 26940ms |
StringBuffer | 35.55ms | 34.87ms | 15.41ms |
StringBuilder | 33.01ms | 31.78ms | 12.82ms |
Windows XP
Класс | HotSpot 1.6.0_20 | JRockit 4.0.1 |
---|---|---|
String | 55260ms | 45330ms |
StringBuffer | 19.38ms | 14.50ms |
StringBuilder | 16.83ms | 12.76ms |
Выводы
Если нам нужны модифицируемые строки, используем StringBuffer (в многопоточных приложениях) и StringBuilder в остальных. Разница весьма ощутима.
Понятно почему JVM от Open JDK немножко отстает, что-то там Sun или уже Oracle подкрутил. Не совсем понятно, почему такая большая разница в работе с объектами класса String под Linux и WinXP. Толи Win не дает JVM работать так же эффективно как Linux, то ли это особенности реализации JVM… Тут нужно копать дальше.
Для справки:
JRockit — это виртуальная машина от Oracle.
HotSpot — это машина изначально разработанная Sun, теперь само собой куплена Oracle.
Open JDK — Open-source JDK основаная на исходниках в свое время открытых Sun-ом.