String Concatenation versus StringBuilder

Sorgsamer Umgang mit dem Speicher reduziert Garbage Collection

String Concatenation vs. StringBuilder - SW Performance Anti-Pattern von Nikolai Moesus | QMETHODS | August 2017
 

Anti-Pattern Beschreibung

String Concatenation vs. StringBuilder - SW Performance Anti-Pattern In der Programmiersprache Java wird meist der Plus-Operator für die Verkettung (Concatenation) von Strings verwendet (bspw. str = str + "more").
Die Zeichenkette einer Instanz der Klasse java.lang.String ist nach der Initialisierung dieser jedoch konstant und kann nicht verändert werden. Stattdessen wird bei der Ausführung des Plus-Operators eine neue String-Instanz erzeugt und so die bisherigen String-Instanzen zur Löschung freigegeben.
Werden String-Verkettungen mit dem Plus-Operator sehr oft aufgerufen, bspw. durch eine Vielzahl von Microservices-Anrufen oder in einer Programmschleife, so wird kurzzeitig eine große Menge an String-Instanzen erzeugt und zeitnah wieder zum Löschen freigegeben. Dieses führt zu einer starken Beanspruchung des Java-Speichers sowie Performanceeinbußen durch die Speicherbereinigungen des Garbage Collectors.
Für eine speichereffiziente Verkettung von Strings stehen in Java die Klassen java.lang.StringBuffer und java.lang.StringBuilder zur Verfügung. Beide Klassen ermöglichen die speichereffiziente Verkettung von Strings mittels der Methoden append und insert.
 

Software-Code Beispiele

Das erste Code-Beispiel zeigt eine in der Praxis typische Variante für die Verkettung von Strings. Diese Implementierung führt zur kontinuierlichen Erzeugung von neuen Objektinstanzen und der zeitnahen Freigabe zur Löschung dieser.
String concatStringsWithPlus () {
	final String[] parts = { "these", "are", "separate", "parts", "of", "a", 
							 "string", "that", "get", "connected" };
	
	String str;
	for (int i = 0; i < 10; ++i) {
		str = str + " " + parts[i];
	}
	return str;
}
Negatives Code-Beispiel: Ineffizient Verknüpfung von Strings mittels Plus Operator
 
Das zweite Code-Beispiel zeigt die Umsetzung mittels der StringBuilder Klasse. Die append-Methode sorgt für eine speicherschonende Verkettung der Strings.
String concatStringsWithBuilder () {
	final String[] parts = { "these", "are", "separate", "parts", "of", "a", 
							 "string", "that", "get", "connected" };
	
	StringBuilder builder = new StringBuilder();
	for (int i = 0; i  10; ++i) {
		builder.append(" ").append(parts[i]);
	}
	return builder.toString();
}
Positiv Code-Beispiel: Effiziente Verkettung von Strings mittels StringBuilder
 

Analyse der Laufzeit

Die Laufzeitanalyse soll uns die Auswirkung der unterschiedlichen Implementierungsvarianten deutlich machen. Die folgende Abbildung zeigt zwei nacheinander mittels des Java Performance Servlets durchgeführte Lastenszenarien. Folgende Einstellungen wurden verwendet:
  • 10^5 Durchläufe 
  • 1 ms Pause 
  • kein Warm-Up 
  • Ausführung einer Garbage Collection vor dem Start 
Der erste Speicheranstieg in der Abbildung wurde mit dem ersten Code-Beispiel unter Verwendung des Plus Operator und der zweite Speicheranstieg mit dem zweiten Code-Beispiel unter Verwendung des StringBuilders ausgeführt. Der Unterschied zwischen der Speicherauslastung und der damit performanceintensiveren Speicherbereinigung ist hier deutlich erkennbar.
 
String Concatenation vs. StringBuilder - SW Performance Anti-Pattern
Abbildung: Auswirkung auf den Speicherverbrauch bei String Concatenation mittels Plus-Operator und StringBuilder
 
Der Softwarecode für das Java Performance Servlet zur Vermessung der Implementierungsvarianten steht im QMETHODS GitHub zur Verfügung. Nutzen sie es, um Experimente in ihrer eigenen Umgebung auszuführen. Zur Vermessung haben wir Dynatrace AppMon eingesetzt.
QMETHODS Github
 

Refactoring Empfehlung

Nach unserer Erfahrung ist die Lesbarkeit des Softwarecodes bei der Verwendung des Plus Operator gegenüber dem StringBuilder für viele Entwickler leichter. Aus diesem Grund empfiehlt sich der Einsatz des StringBuilder vor allem in hochfrequentierten Code-Bereichen. In der Regel sind das ...
  • hochfrequentierte Methoden, 
  • umfangreiche Programmschleifen (for, while) oder 
  • rekursive Aufrufe. 
Für diese Fälle würden wir ein entsprechendes Refactoring dringend empfehlen!
 
Zurück zur Anti-Pattern Übersichtseite



Ihr Kommentar zum Blog-Beitrag

Nennen Sie uns bitte Ihren Namen und E-Mail-Adresse zur Verifikation. Die E-Mail Adresse wird nicht veröffentlicht!