wechselwirkungen zwischen performance und architektur ... · more computing sins are committed in...
TRANSCRIPT
MIN-FakultätDepartment Informatik
Diplomarbeit
Wechselwirkungen zwischen Performance undArchitektur
–Analyse, Optimierung und Evaluation am
Beispiel des JCommSy
Salim RECHENBERG Februar 2009
[email protected] InformatikMatr.-Nr. 5596126Fachsemester 10
Erstgutachter: Professor Dr. Norbert RITTER
Zweitgutachter: Dr. Wolf-Gideon BLEEK
More computing sins are committed in the name of efficiency (without necessarilyachieving it) than for any other single reason – including blind stupidity.– William A. Wulf (1972)
i
Abstract
Software architecture is an important aspect of software development. The architecture describes
the structure of the software and improves the communication between those involved in the pro-
ject. Performance is an important runtime aspect of software upon which the software’s acceptance
and thus its success may depend.
A structured architecture induces additional indirections, e. g. more objects and method calls. If
one subsystem is decoupled from another by a third, such indirections are necessary.
Interdependency between architecture and performance of the web application JCommSy is ana-
lysed. Architectural flaws are identified and the resulting refactorings are described. Performance
is analysed by using a profiler. Performance hotspots are identified and optimisations are perfor-
med. The effects of the refactorings and optimisations on both, performance and architecture, are
discussed.
The performance scale is divided into five categories. Indirections are in category I (about 10
nanoseconds) and do not affect the overall performance if category IV-aspects (milliseconds) are
present. Web applications are highly shaped by I/O-operations (IV). Adding an extra layer has no
impact on the performance. For the JCommSy a performance speed-up of 61 percent is achieved
without harming the architecture. Some optimisations increase the complexity, but there is no need
to avoid using indirections for performance reasons.
Abstract (deutsch)
Software-Architektur ist ein wichtiger Aspekt von Software-Entwicklung. Die Architektur be-
schreibt die Struktur der Software und verbessert die Kommunikation zwischen den Projektbetei-
ligten. Performance ist eine wichtige Laufzeiteigenschaft, die über die Akzeptanz der Software
und damit über den Projekterfolg entscheiden kann.
Saubere Architektur resultiert in zusätzlichen Indirektionen, wie z. B. in zusätzlichen Objekten und
Methodenaufrufen. Sieht die Architektur eine Entkopplung zweier Subsysteme durch ein drittes
vor, sind derartige Indirektionen nötig.
Die Wechselwirkungen zwischen Architektur und Performance werden an der Web-Anwendung
JCommSy analysiert. Architekturverletzungen werden identifiziert und die resultierenden Refac-
torings beschrieben. Die Performance wird mit einem Profiler untersucht. Performance-Hotspots
werden identifiziert und entsprechende Optimierungen durchgeführt. Die Auswirkungen der Re-
factorings und Optimierungen auf Performance und Architektur werden diskutiert.
Es werden fünf Kategorien der Performance-Skala eingeführt. Indirektionen haben Auswirkungen
in Kategorie I (ca. 10 Nanosekunden). Sie haben keine Auswirkungen auf die Gesamtperformance,
wenn Kategorie IV-Aspekte (Millisekunden) ebenfalls vorhanden sind. Web-Anwendungen sind
geprägt durch E/A-Operationen (IV). Eine zusätzliche Schicht hat keine Auswirkungen auf die
Performance. Die Performance des JCommSy kann um 61 Prozent verbessert werden, ohne dass
dabei die Architektur negativ beeinflusst wird. Es gibt Optimierungen, die die Komplexität erhö-
hen, aber Indirektionen sollten nicht aus Gründen der Performance vermieden werden.
ii
iii
Danksagung
Zuallererst danke ich meinen Betreuern Professor Dr. Norbert Ritter und Dr. Wolf-Gideon Bleek.
Herrn Ritter danke ich dafür, dass er die Erstbetreuung übernommen hat. Seine Anmerkungen,
seine kritischen Fragen und seine aktive Beteiligung bei meinem Diplomarbeitsvortrag waren von
großem Wert. Vielen Dank.
Obwohl diese Diplomarbeit in eine Zeit des Umbruchs in Wolf-Gideons beruflichem Weg gefallen
ist, hat er mir stets zur Seite gestanden und zahlreiche Stunden seiner Zeit zur Verfügung gestellt.
Er hat es vortrefflich verstanden, mich in den richtigen Momenten anzustoßen, mir die Richtung
zu weisen und mir gleichzeitig maximalen Gestaltungsspielraum zu lassen. Seine fachliche Kom-
petenz und sein persönlicher Einsatz und Zuspruch, waren maßgebend für die Entstehung dieser
Arbeit. Vielen Dank, Wolf-Gideon.
Dipl.-Inform. Uwe Zimmer danke ich für die technische Unterstützung. Er hat mir die nötige
Hard- und Software bereitgestellt und ich konnte mich stets darauf verlassen, dass meinen Bitten
bestmöglich entsprochen wurde. Dies war von großem Wert. Vielen Dank, Uwe.
Ilker Daricili und Sönke Thiesen, meinen Weggefährten und Freunden, danke ich besonders. Wir
haben etliche Stunden gemeinsam in unserem Büro verbracht. Durch eure Anwesenheit konnte ich
innerhalb von Minuten Antworten auf Fragen erhalten und in Diskussionen meine Thesen finden
und festigen. Auch menschlich seid ihr eine große Bereicherung und Stütze für mich. Nicht zuletzt
unsere gemeinsamen Pausen haben für den nötigen Ausgleich gesorgt.
Sönke, ich danke dir für deine schonungslose Kritik und deine Bereitschaft alles zu hinterfragen.
Du hast mir sowohl menschlich, als auch fachlich, stets den richtigen Impuls im richtigen Moment
gegeben.
Ilker, dir danke ich für die zahlreichen inhaltlichen Diskussionen, für deine unermüdliche Bereit-
schaft meine Texte Korrektur zu lesen und sie um unzählige Kommas zu bereichern. Auf dich war
immer Verlass.
Jungs, wir sind ein starkes Team.
Anne, dir danke ich besonders für das Korrekturlesen meiner Arbeit. Du hast dich Wort für Wort
durch meine Texte gekämpft und zahlreiche sprachliche Krücken und unpräzise Formulierungen
korrigiert. Ganz herzlichen Dank.
Zum Schluss danke ich meiner Familie und meinen Freunden. Ihr alle habt mir während des letzten
halben Jahres den nötigen Platz gelassen und ward dennoch für mich da, wenn ich euch brauchte.
Vielen Dank.
iv
Inhaltsverzeichnis v
Inhaltsverzeichnis
1 Einleitung 11.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Zielsetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Gliederung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Grundlagen 52.1 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.1.1 Architekturelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.1.2 Architekturstile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.3 Saubere Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.1.4 Gründe für die Relevanz von Architektur . . . . . . . . . . . . . . . . . 8
2.2 Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.1 Zeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.2 Speicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.3 Nebenläufigkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2.4 Gute Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2.5 Optimierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2.6 Benchmarking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2.7 Profiling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2.8 Performance-Modell in Java . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3 JCommSy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3.1 Web-Anwendung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3.2 CommSy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3.3 Migrationsprojekt JCommSy . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3.4 Performance als Kriterium . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.4 Eingesetzte Technologien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.4.1 JavaBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.4.2 Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.4.3 Java Server Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4.4 Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4.5 Hibernate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4.6 Spring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3 Verwandte Themen 253.1 Performance-Modelle und Performance Engineering . . . . . . . . . . . . . . . 25
vi Inhaltsverzeichnis
3.1.1 Architektur- und Performance-Modelle . . . . . . . . . . . . . . . . . . 25
3.1.2 Software architecture-based performance engineering . . . . . . . . . . . 26
3.2 Generische Leistungsbewertung einer Mehrschichtenarchitektur . . . . . . . . . 27
3.3 Performance-Antipattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4 Architekturanalyse 294.1 Ziel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.1 Abgrenzung: Architekturbewertung . . . . . . . . . . . . . . . . . . . . 29
4.2 Software-Werkzeuge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2.1 Sotograph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2.2 Sotoarc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2.3 Alternativen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3 Vorgehen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3.1 Allgemeines zum Vorgehen . . . . . . . . . . . . . . . . . . . . . . . . 33
4.3.2 Vorgehen beim JCommSy . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.4 Architektur des JCommSy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.4.1 Architekturmodell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.4.2 Verletzungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5 Performance-Analyse 395.1 Ziel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.2 Vorgehen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.2.1 Testumgebung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.2.2 Einflüsse des Tomcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.2.3 Testszenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.3 Software-Werkzeuge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.3.1 JMeter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.3.2 JProfiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.3.3 Alternativen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.4 Betrachtungsebenen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.4.1 Performance-Kategorien und -Bausteine . . . . . . . . . . . . . . . . . . 48
5.4.2 Innerhalb einer Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5.4.3 Zusammenspiel von Klassen . . . . . . . . . . . . . . . . . . . . . . . . 51
5.4.4 Architekturelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.5 Performance des JCommSy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
5.5.1 Zeitliche Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
5.5.2 Speicherverbrauch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.5.3 Auswirkungen von Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.5.4 Bewertung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Inhaltsverzeichnis vii
6 Ergebnisse der Refactorings und Optimierungen am JCommSy 596.1 Durchgeführte Refactorings und Optimierungen . . . . . . . . . . . . . . . . . . 59
6.1.1 Deserialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.1.2 Fachlogik extrahieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
6.1.3 Spring Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . 67
6.1.4 FileServiceHibernate erweitern . . . . . . . . . . . . . . . . . . . . . . 70
6.1.5 StringBuilder benutzen . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
6.2 Auswirkungen unterschiedlicher JVM-Modi . . . . . . . . . . . . . . . . . . . . 73
6.3 Auswirkungen auf Performance und Architektur . . . . . . . . . . . . . . . . . . 74
6.3.1 Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.3.2 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
6.4 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
7 Allgemeine Schlussfolgerungen 797.1 Externe Bibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
7.1.1 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
7.1.2 Rahmenwerke allgemein . . . . . . . . . . . . . . . . . . . . . . . . . . 80
7.2 Vorgehen beim Optimieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.2.1 Optimierungen durch Messungen belegen . . . . . . . . . . . . . . . . . 81
7.2.2 In den hohen Kategorien zuerst optimieren . . . . . . . . . . . . . . . . 81
7.2.3 Flaches Profil anstreben . . . . . . . . . . . . . . . . . . . . . . . . . . 82
7.2.4 Performance ist wichtig . . . . . . . . . . . . . . . . . . . . . . . . . . 82
7.3 Auswirkungen von Performance-Optimierungen auf die Architektur . . . . . . . 83
7.3.1 Optimierungen erhöhen die Komplexität . . . . . . . . . . . . . . . . . . 83
7.4 Auswirkungen von sauberer Architektur auf die Performance . . . . . . . . . . . 84
7.4.1 Ausbau von Schichtung oder Einführung von Entwurfsmustern . . . . . . 84
7.4.2 Softwaretechnische Prinzipien . . . . . . . . . . . . . . . . . . . . . . . 85
7.4.3 Performance durch Einhaltung der Architektur . . . . . . . . . . . . . . 85
7.5 Datenbanken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
7.5.1 Zugriffe bündeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
7.5.2 OR-Mapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7.5.3 Datenbankschema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
8 Schlussbemerkungen 898.1 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.2 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Literaturverzeichnis 93
A Messergebnisse 99
B Testfälle 103
viii Inhaltsverzeichnis
Abbildungsverzeichnis ix
Abbildungs- und Tabellenverzeichnis
Abbildungsverzeichnis
2.1 Aufbau der Sun HotSpot JVM [Sun99] . . . . . . . . . . . . . . . . . . . . . . 19
4.1 JCommSy im Sotoarc (Benutzungsoberfläche) . . . . . . . . . . . . . . . . . . . 31
4.2 Architektur des JCommSy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.1 Ausschnitt aus der JMeter-Benutzungsoberfläche . . . . . . . . . . . . . . . . . 43
5.2 Benutzungsoberfläche des JProfiler . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.3 Gegenüberstellung von Ausführungszeiten und Einteilung in die Kategorien . . . 50
6.1 Sequenzdiagramm einer Anfrage vor dem Refactoring (Ausschnitt) . . . . . . . . 62
6.2 Sequenzdiagramm einer Anfrage nach dem Refactoring (Ausschnitt) . . . . . . . 63
6.3 Schematischer Ablauf einer Anfrage an das JCommSy . . . . . . . . . . . . . . 64
6.4 Schematischer Ablauf einer Anfrage für eine Bearbeitenseite . . . . . . . . . . . 65
A.1 Durchschnittliche Antwortzeiten des JCommSy ohne Profiler . . . . . . . . . . . 100
A.2 Gegenüberstellung der minimalen Antwortzeiten mit und ohne Ajax . . . . . . . 101
A.3 Gegenüberstellung der durchschnittlichen Antwortzeiten mit und ohne Ajax . . . 101
Tabellenverzeichnis
5.1 Laufzeiten der Schichten (Stand: 14. Oktober 2008) . . . . . . . . . . . . . . . 54
5.2 Speicherallokationen pro Schichten (Stand: 14. Oktober 2008) . . . . . . . . . . 55
6.1 Vergleich der Ausführungszeiten der Schichten (Stand: 19. Dezember 2008) . . . 75
A.1 Vergleich der unterschiedlichen JVM Modi (Stand: 2. Dezember 2008) . . . . . 99
x Tabellenverzeichnis
1
Kapitel 1
Einleitung
Performance ist eine Eigenschaft von Software, mit der jeder Computer-Benutzer konfrontiert ist.
Normalerweise nimmt er sie erst dann wahr, wenn sie den Ansprüchen nicht mehr genügt. Im Ge-
gensatz dazu wird die Architektur einer Software vom Benutzer gar nicht direkt wahrgenommen.
Entwickler hingegen, beschäftigen sich meist explizit mit Architektur. Während Architekturbe-
trachtungen regelmäßig zum Entwicklungsprozess dazugehören, wird der Fokus selten systema-
tisch auf Performance gerichtet. Aufgrund der täglichen Erfahrung mit Software, versuchen Ent-
wickler ständig im Kleinen und bereits während des Programmierens zu optimieren. Solche Opti-
mierungen verringern die Qualität des Quelltextes.
Es ist viel Halbwissen über Performance im Umlauf, was zusammen mit den eigenen Erfahrungen
über langsame Software dazu führt, dass bereits während der Entwicklung und ohne Messungen
optimiert wird. In diesem scheinbaren Spannungsfeld zwischen Architektur und Performance be-
wegt sich diese Arbeit.
1.1 Motivation
Es ist allgemein bekannt, dass Architektur ein wichtiger Aspekt von Software ist, indem sie z. B.
das Systemverständnis unterstützt. Des Weiteren bleibt Software durch eine saubere Architektur
wartbarer, erweiterbarer und wiederverwendbarer.
Unabhängig von expliziten Anforderungen des Kunden, ist jedem Entwickler durch seine eigenen
Erfahrungen bewusst, dass Performance eine Eigenschaft von Software ist, die über deren Akzep-
tanz entscheiden kann.
In dieser Arbeit werden die beiden Aspekte Architektur und Performance zusammengebracht und
untersucht, welche Auswirkungen sie aufeinander haben. Eine saubere Architektur resultiert in der
Regel in zusätzlichen Indirektionen. Andererseits können Direktzugriffe nötig sein, um die Per-
formance zu verbessern. Eine schlecht entworfene Architektur kann die Performance begrenzen.
Diese scheinbaren Widersprüche sind die Grundlage für die im nächsten Abschnitt vorgestellten
und im Laufe der Arbeit behandelten Fragestellungen.
Die betrachtete Software, das JCommSy, ist eine Web-Anwendung mit einer ausgefeilten Archi-
tektur. Sie ist hinreichend komplex, beinhaltet Datenbankzugriffe und integriert eine Reihe von
Rahmenwerken.
2 Kapitel 1 Einleitung
1.2 Zielsetzung
Im Rahmen dieser Arbeit wird dargestellt, wie man Architektur und Performance einer Software
analysiert. Es wird untersucht, wo innerhalb einer Web-Anwendung die für die Performance aus-
schlaggebenden Bereiche liegen. Dies wird auf Architekturebene geschehen.
Nachdem diese Bereiche identifiziert sind, werden dafür Optimierungen untersucht und durchge-
führt. Dabei wird die Performance der Anwendung verbessert werden. Trotzdem ist die absolute
Performance nicht das Primärziel dieser Arbeit. Vielmehr geht es darum, herauszuarbeiten, wo
und wie optimiert werden kann, bzw. an welchen Stellen Optimierungen keinen Mehrwert brin-
gen. Auch Erkenntnisse bezüglich des Vorgehens beim Optimieren werden Ergebnis der Arbeit
sein.
Neben der Performance wird die Architektur der Anwendung verbessert werden. Sie wird ana-
lysiert, um Bereiche zu identifizieren, die verbessert werden können. Die daraus resultierenden
Refactorings werden umgesetzt und das Vorgehen und die Ergebnisse dargestellt. Neben der Ist-
Architektur der Anwendung wird auch die Soll-Architektur weiter ausgearbeitet und eine aktuelle
Dokumentation selbiger entstehen.
Anhand der Optimierungen und Refactorings werden Zusammenhänge zwischen Performance und
Architektur aufgedeckt. Es wird gezeigt werden, inwieweit die durch eine ausgeprägte Architek-
tur oder den Einsatz von Entwurfsmustern hinzukommenden Indirektionen, Auswirkungen auf die
Performance einer Web-Anwendung haben. Ebenso werden die Auswirkungen von Optimierun-
gen auf die Architektur und die Komplexität der Software untersucht und dargestellt.
1.3 Gliederung
Der Aufbau dieser Arbeit wird im Folgenden dargestellt. Dies erlaubt eine Einordnung der Kapitel
und bietet die Möglichkeit gezielt bestimmte Kapitel herauszugreifen.
In dem Kapitel Grundlagen werden für diese Arbeit wichtige Begriffe zu den Themen Architektur
und Performance definiert und zentrale Technologien eingeführt. Der Untersuchungsgegenstand
JCommSy wird vorgestellt.
Das Kapitel Verwandte Themen gibt eine Übersicht über weitere Arbeiten die sich mit dem Zu-
sammenspiel von Architektur und Performance beschäftigen. Dieses Kapitel ist keine Grundlage
für das Verständnis der nachfolgenden Kapitel.
Auf der Basis des Grundlagen-Kapitels wird im Architekturanalyse-Kapitel die Architektur des
JCommSy analysiert. Die dafür nötigen Werkzeuge werden vorgestellt und die Ergebnisse darge-
stellt.
Im Kapitel Performance-Analyse wird die Performance des JCommSy untersucht. Das Vorgehen
und die dafür nötigen Werkzeuge werden eingeführt und die unterschiedlichen Ebenen, auf denen
Performance betrachtet werden kann, dargelegt. Abschließend werden die Ergebnisse der Analyse
beschrieben. Dieses Kapitel bezieht sich auf den Zustand des JCommSy zu Beginn dieser Arbeit.
Das Kapitel Ergebnisse der Refactorings und Optimierungen am JCommSy beschäftigt sich mit
1.4 Notation 3
den identifizierten Optimierungen und Refactorings. Sie werden einzeln motiviert, die Durch-
führung beschrieben und die Ergebnisse dargestellt. Abschließend wird das JCommSy nach Ab-
schluss aller Optimierungen mit dem Zustand davor verglichen.
Die Ergebnisse werden in Allgemeine Schlussfolgerungen vom JCommSy abstrahiert. Das Kapitel
beschreibt die gewonnenen Erkenntnisse und bewertet sie.
In Schlussbemerkungen wird eine abschließende Zusammenfassung gegeben. Sie besteht aus ei-
nem Ausblick, der offene Punkte erörtert, und einem Fazit.
Liegt das Interesse insbesondere bei den Ergebnissen die das JCommSy betreffen, dann sind die
Abschnitte 2.3, 4.4 und 5.5 sowie das Kapitel 6 von zentraler Bedeutung. Liegt das Interesse am
JCommSy im Hintergrund, können diese Abschnitte ausgelassen werden und der Schwerpunkt
insbesondere auf das Kapitel 7 gelegt werden.
Innerhalb der Abschnitte und Kapitel wird es Verweise zu weiterführende Informationen geben,
sowohl innerhalb dieser Arbeit, als auch auf externe Quellen. Jedes Kapitel schließt mit einer
Zusammenfassung ab, die die Kernaussagen enthält.
1.4 Notation
Im Rahmen dieser Arbeit wird auf eine explizite Erwähnung der weiblichen Formen verzichtet.
Natürlich sind z. B. mit Entwicklern immer auch Entwicklerinnen gemeint.
Programmiersprachliche Konstrukte, wie beispielsweise Klassen- oder Packagenamen, werden in
dieser Schrift geschrieben.
4 Kapitel 1 Einleitung
5
Kapitel 2
Grundlagen
In diesem Kapitel werden die für diese Arbeit benötigten, grundlegenden Begriffe eingeführt und
definiert. Zentrale Aspekte sind Software-Architektur und Performance, die ausführlich behandelt
werden. Im Performance Abschnitt wird dabei die Java Virtual Machine unter diesen Aspekten
eingeführt. Der Untersuchungsgegenstand dieser Diplomarbeit, das JCommSy, wird vorgestellt,
sowie der Hintergrund aus dem es entstanden ist beleuchtet. Abgeschlossen wird dieses Kapitel
mit der Einführung einiger Technologien, die im Laufe dieser Arbeit von Bedeutung sind.
2.1 Architektur
Einer der zentralen Begriffe dieser Arbeit ist Software-Architektur. Es gibt unterschiedliche Defi-
nitionen für Software-Architektur. Ich folge der Definition von Reussner und Hasselbring, welche
eine Übersetzung der Definition des ANSI/IEEE Standards [Gro00] ist:
Definition 2.1 (Software-Architektur) „Die Software-Architektur ist die grundlegende Organi-
sation eines Systems, dargestellt durch dessen Komponenten, deren Beziehungen zueinander und
zur Umgebung, sowie die Prinzipien, die den Entwurf und die Evolution des Systems bestimmen.“
[RH06, S. 1]
Ein Kernaspekt von Software-Architektur ist, dass die Struktur oder die Strukturen des Systems
als Ganzes im Fokus sind und nicht Teilaspekte wie z. B. das Zusammenspiel einzelner Klassen.
Dies wird auch in der alternativen Definition von Software-Architektur von Bass, Clements und
Kazman so gesehen [BCK98, S. 23]. Der Begriff Komponente ist explizit weit gefasst und stellt
eine Abstraktion eines Teilaspekts der Software dar. Er ist mit Absicht nicht genauer definiert, weil
er unterschiedliche Formen annehmen kann, z. B. Prozesse, Datenbanken oder Packages [BCK98,
S. 23]. Ebenso variieren die Beziehungen der Komponenten, sie können z. B. die Verkörperung
einer Benutzt-Beziehung sein.
In dieser Arbeit verwende ich statt des etwas sperrigen Begriffs Software-Architektur die verkürz-
te Form Architektur.
Jede Software hat eine Architektur, die jedoch nicht immer bekannt sein muss. Damit eine Ar-
chitektur kommuniziert werden kann, sollte sie nicht nur in den Köpfen einzelner Entwickler
vorliegen, sondern durch eine Architekturbeschreibung explizit dokumentiert werden. Eine Archi-
tekturbeschreibung setzt sich aus einem oder mehreren Modellen zusammen (siehe [RH06, S. 36]
und [Gro00]), welche von der Software abstrahieren und dabei einen oder mehrere Aspekte der
Software verdeutlichen und andere ausblenden.
6 Kapitel 2 Grundlagen
Der ANSI/IEEE Standard [Gro00] definiert den Begriff Sicht folgendermaßen: Eine Sicht ist ei-
ne Menge von Modellen, die ein System zu einem bestimmten Aspekt (der Standard spricht hier
von Standpunkten) beschreiben (siehe auch [RH06, S. 36]). Unterschiedliche Autoren definieren
unterschiedliche Standpunkte, wobei ich in dieser Arbeit der Unterteilung in statischer Stand-
punkt, dynamischer Standpunkt und Verteilungs-Standpunkt folgen werde, wie sie von Reussner
und Hasselbring vorgenommen wird [RH06, S. 38]. Vom dynamischen Standpunkt aus wird das
Laufzeitverhalten, wie z. B. die benötigte Ausführungszeit, in den Fokus gerückt. Betrachtet man
eine Architektur von dem Verteilungs-Standpunkt aus, dann ist z. B. von Interesse, welche Pro-
zesse auf welchen Rechnern ausgeführt werden und wie diese Rechner miteinander verbunden
sind. Vom statischen Standpunkt aus erkennt man die Zerlegung des Systems in Komponenten
(Architekturelemente) und deren Abhängigkeiten untereinander.
2.1.1 Architekturelemente
Betrachtet man eine Architektur aus einer statischen Sicht, spricht man anstelle von Komponen-
te, im Sinne der Definition 2.1, von Architekturelement. In der dynamischen und verteilten Sicht
bleibt weiterhin der Begriff Komponente in Verwendung und beschreibt dort ein ausführbares Ar-
tefakt (siehe [Lil08, S. 23]).
In objektorientierten Programmiersprachen entsprechen die Architekturelemente Klassen oder
Subsystemen. Nicht objektorientierte Sprachen werden in dieser Arbeit nicht betrachtet. Neben
diesen zwei Arten von Architekturelementen gibt es drei Arten von Beziehungen: Enthaltensein-,
Benutzt- und Vererbungs-Beziehung. Auf Klassenebene entsprechen die Beziehungen den aus der
Objektorientierung bekannten Beziehungen. Eine Klasse benutzt z. B. eine andere, wenn sie eine
Methode an einem Objekt dieser Klasse aufruft.
Auf der Subsystemebene wird die Enthaltensein-Beziehung wie folgt definiert: Ein Subsystem
kann in einem Subsystem enthalten sein und eine Klasse kann in einem Subsystem enthalten sein.
An dieser Stelle unterscheiden sich Subsysteme von den in Java zur Verfügung stehenden Pack-
ages. Packages können zwar Klassen, jedoch keine anderen Packages enthalten. Die in der realen
Nutzung häufig anzutreffenden hierarchischen Namensräume legen zwar nahe, dass ein Package
in einem anderen enthalten sein kann, jedoch wird dies von der aktuellen Sprachspezifikation nicht
unterstützt. Zukünftig sollen Superpackages die Sprache um ein solches Konstrukt erweitern, mit
dem sich Strukturen oberhalb von Packages aufbauen lassen (siehe [BH09]). Die Benutzt- und
Vererbungsbeziehungen zwischen Subsystemen werden wie folgt definiert: Ein Subsystem benutzt
ein anderes, wenn eine der (indirekt) enthaltenen Klassen eine Klasse aus dem anderen Subsystem
benutzt (und entsprechend für die Vererbungsbeziehung). Diese Definition folgt der Definition von
Lilienthal [Lil08, S. 27 ff.].
Neben Architekturelementen und Beziehungen definiert Lilienthal Schnittstellen als Ausdrucks-
mittel [Lil08, S. 24]. Auf Klassenebene umfasst eine Schnittstelle die Menge aller Methoden, die
an einer Klasse von einer anderen Klasse aufgerufen werden können. Die Schnittstelle eines Sub-
systems wird analog durch alle in ihr enthaltenen Architekturelemente gebildet, die von außerhalb
2.1 Architektur 7
des Subsystems sichtbar sind.
2.1.2 Architekturstile
Ein Architekturstil ist eine Lösungsstruktur, die für die Elemente einer Architektur möglichst
durchgängig angewendet werden soll [RH06, S. 87]. Ein Architekturstil enthält Regeln für die
Architekturelemente und deren Beziehungen untereinander [BCK98, S. 25]. Beispielsweise ent-
hält der Stil Schichtenarchitektur die Regel, dass es nur eine Architekturelementart gibt: Schicht.
Für die Beziehungen zwischen Schichten gibt es die Einschränkung, dass eine Schicht über einer
anderen liegt und die Benutzung nur von oben nach unten erlaubt ist.
Ein Architekturstil kann als Abstraktion über eine Menge von Architekturen betrachtet werden,
deren Gemeinsamkeiten in einem oder mehreren Aspekten identifiziert und beschrieben werden
und als Struktur für zukünftige Projekte festgehalten werden (siehe [BCK98, S. 94]). Ein Architek-
turstil verhindert, dass ein beliebiges Design verwendet wird und bringt Struktur in die Software.
Er stellt damit eine „Einschränkung des Designraums“ dar [Lil08, S. 29].
Beispiele für Architekturstile sind Schichtenarchitektur, Subsystemarchitektur oder Referenzar-
chitektur. In einer Schichtenarchitektur gibt es das Architekturelement Subsystem, welches hier
als Schicht bezeichnet wird. Höhere Schichten dürfen untere Schichten benutzen, aber nicht an-
ders herum. Schichten können Schnittstellen aufweisen und dürfen nur über diese benutzt werden.
Auf diese Weise ergibt sich für die entstehende Software eine Struktur, in der u. a. die Klassen
unterer Schichten nicht von denen oberer Schichten abhängen.
Subsystemarchitekturen bestehen aus Subsystemen. Subsysteme stehen in Beziehung zueinander,
wenn Klassen eines Subsystems Klassen des anderen benutzen. Eine Regel, die die erlaubten Be-
ziehungen einschränkt, ist Zyklenfreiheit. Für die Subsysteme kann spezifiziert sein, welche Teile
eines Subsystems zu der öffentlichen Schnittstelle gehören und welche Teile nicht von anderen
Subsystemen benutzt werden dürfen.
Für Referenzarchitekturen sind die Elemente keine Subsysteme, sondern Klassen. Zum Beispiel
legt die Referenzarchitektur Werkzeug-Automat-Material (WAM) fest, dass es u. a. Werkzeuge
und Materialien gibt [Z+98]. Für die Material-Klassen gilt die Einschränkung, dass sie keine
Werkzeug-Klassen benutzen dürfen. Für eine Referenzarchitektur sind die Regeln also auf Klas-
senebene. Es ist möglich, dass eine Software sowohl den Stil Schichtenarchitektur als auch Refe-
renzarchitektur in ihrer Architektur vereinigt. Mit Referenzarchitekturen lassen sich komplexere
und genauere Vorgaben machen, als dies mit einer Schichtenarchitektur möglich ist.
Architekturstile sind insofern ähnlich zu Entwurfsmustern (siehe [GHJV95]), als dass sie eine be-
währte, allgemeine Struktur darstellen, die nach Bedarf eingesetzt werden kann, um das System zu
strukturieren und u. a. leichter kommunizierbar und wartbar zu machen. Aus diesem Grund wird
der Begriff Architekturmuster von einigen Autoren synonym zu Architekturstil verwendet (siehe
z. B. [RH06, S. 342]).
8 Kapitel 2 Grundlagen
2.1.3 Saubere Architektur
Im Laufe dieser Arbeit, wird die Architektur einer Software analysiert, Veränderungen an dieser
durchgeführt und evaluiert. Eine Architektur an sich ist weder schlecht noch gut. Um die Ar-
chitektur bewerten zu können, müssen zunächst die Begriffe Soll-Architektur und Ist-Architektur
definiert werden. Die Soll-Architektur umfasst die Konzepte und Modelle, die das Entwicklungs-
team der betrachteten Software sich als Zielvorstellung überlegt hat. Als Grundlage dafür wird
häufig ein Architekturstil wie z. B. Schichtenarchitektur verwendet. Nicht immer wird die Soll-
Architektur explizit gemacht und in Dokumenten festgehalten.
Um die Ist-Architektur der Software zu bestimmen, sind folgende Schritte nötig: Aus dem Quell-
text muss ein Modell extrahiert werden. Dieses kann zusammen mit einer Abbildungsvorschrift,
die aus Soll-Architektur und Architekturstil gewonnen wird, zu einer Ist-Architektur angereichert
werden (siehe [Lil08, S. 39]). Beispielsweise muss aus dem Quelltext extrahiert werden, welche
Klassen eines Packages andere Klassen anderer Packages benutzen. Nachdem die einzelnen Pack-
ages mit Hilfe der Soll-Architektur den entsprechenden Architekturelementen zugeordnet worden
sind, erhält man die Beziehungen der Architekturelemente in der Ist-Architektur. Weder das Zu-
ordnen, noch das Extrahieren sind triviale Arbeitsschritte. Das Extrahieren kann z. B. mit Hilfe
von Software-Werkzeugen, wie dem Sotograph (siehe 4.2.1), erfolgen, das Zuordnen muss aber
meistens manuell erfolgen. Es gibt Ansätze, wie von Scharping, die Zuordnung z. B. über Na-
menskonventionen oder Java-Annotationen automatisch zu ermöglichen [Sch08a].
Auf Basis der Ist-Architektur kann die Architektur des Softwaresystems bewertet werden. Da-
für kann sie mit der Soll-Architektur oder dem Architekturstil verglichen werden. Abweichungen
stellen Architekturverletzungen dar. Auf dieser Grundlage, kann dann mit Hilfe eines Qualitätsmo-
dells und einer Gewichtung der unterschiedlichen Qualitätsmerkmale eine Architekturbewertung
durchgeführt werden (siehe Kapitel 4.1.1). Beispiele für Softwarequalitätsmerkmale sind Perfor-
mance und Wartbarkeit.
2.1.4 Gründe für die Relevanz von Architektur
In diesem Abschnitt werden einige Gründe vorgestellt, warum Architektur ein wichtiger Aspekt
von Software ist, insbesondere bei wachsenden Systemen. Es gibt drei Hauptvorteile, die sich aus
der Betrachtung von Architektur ergeben (siehe [BCK98, S. 28] und [CKK02, S. 2]):
1. Kommunikation unter allen Beteiligten
2. Verkörperung früher Design-Entscheidungen
3. Wiederverwendbare Abstraktion über ein System
Die Architektur ermöglicht eine Kommunikation unter den Beteiligten, indem ein gemeinsames
Systemverständnis auf einer konzeptionell hohen Ebene vorliegt. So kann ein Manager aus einer
Architektur ableiten, wie gut sich die Arbeiten parallelisieren lassen, während ein Kunde abschät-
zen kann, wie umfangreich das System wird. Generell kann die Kommunikation auf einer höheren
2.2 Performance 9
Ebene erfolgen, indem z. B. über Schichten anstatt von Packages oder gar Klassen gesprochen
wird. Auch ermöglicht das Herausarbeiten der Architektur ein gemeinsames Vokabular, ähnlich
wie es durch Entwurfsmuster möglich ist [GHJV95].
Die Architektur verkörpert die frühen Design-Entscheidungen und bietet eine Grundlage zum
Analysieren und Bewerten. Insbesondere die frühen Design-Entscheidungen haben lange Bestand,
bis hin in die Wartungsphase. Sie müssen nicht nur früh im Prozess getroffen werden, sondern sind
später schwierig zu ändern. Es ist wichtig, diese Entscheidungen möglichst fundiert zu treffen. Die
Architektur ist nicht nur Grundlage für die Struktur des implementierten Systems, sondern auch
für die Struktur des Teams, welches anhand der Architektur in Arbeitsgruppen aufgeteilt wird.
Die Architektur begünstigt, ermöglicht oder verhindert Qualitätseigenschaften des Systems. So ist
z. B. die Veränderbarkeit des Systems von der Kapselung einzelner Subsysteme abhängig. Damit
lassen sich anhand der Architektur Aussagen über die zu erwartenden Qualitätseigenschaften des
Systems machen. Auf dieser Basis können bereits in der Anfangsphase des Projekts sich wider-
sprechende Qualitätsziele diskutiert werden.
Die Architektur erleichtert das Entwickeln von Prototypen, mit denen z. B. die Performance des
Systems frühzeitig abgeschätzt werden kann. Damit sinkt das Risiko, dass das Projekt in späteren
Entwicklungsphasen scheitert.
Die Architektur ist abstrakt genug, um auf andere (ähnliche) Systeme übertragen werden zu kön-
nen und somit wiederverwendet zu werden. Dabei können die Konsequenzen für das entstehende
System mit übernommen werden, also z. B. spezielle Qualitätseigenschaften.
Unter dem Schlagwort „architecture-based development“ versteht man, dass der Fokus weniger
auf dem Programmieren als auf dem Zusammenstellen von Komponenten basiert, welche jeweils
einzeln und unabhängig voneinander entwickelt werden können.
2.2 Performance
In diesem Abschnitt wird der Begriff Performance eingeführt und die Art, wie er in dieser Ar-
beit verwendet wird, abgegrenzt. Es werden die Grundlagen der Optimierung eingeführt. Profiling
wird motiviert und das Vorgehen vorgestellt, wobei auf Besonderheiten bezüglich Java eingegan-
gen wird.
Der englische Begriff Performance lässt sich in vielerlei Hinsicht ins Deutsche übersetzen, z. B.
Auftritt, Leistung, aber auch Effizienz. Unpassend ist die Übersetzung nach „Performanz“, da
dieses Wort im Deutschen nur im Kontext der Sprachwissenschaften existiert [WKKRSS01]. Ich
verwende deswegen in dieser Arbeit das Wort Performance. Fischer und Hofer definieren Perfor-
mance als
„mit irgendwelchen standardisierten oder willkürlichen Massstäben [sic] beurteilte
Leistungsfähigkeit eines Computersystems und/oder seiner Teile“ [FH08, S. 621].
10 Kapitel 2 Grundlagen
Den Begriff Performance1 definiere ich für diese Arbeit etwas spezieller und auf Softwaresysteme
zugeschnitten wie folgt:
Definition 2.2 (Performance) Die Performance eines Softwaresystems ist dessen messbare Leis-
tungsfähigkeit und beinhaltet dessen Zeit- und Verbrauchsverhalten.
Unter dem Zeitverhalten verstehe ich sowohl die Antwort- und Verarbeitungszeiten als auch den
Durchsatz des Systems. Die Antwortzeit umfasst die Zeit zwischen Eingang eines Ereignisses und
Reaktion des Systems. Am Beispiel einer Desktopanwendung kann dies z. B. die Zeit umfassen,
die zwischen dem Zeitpunkt an dem der Benutzer auf den Laden-Button klickt und dem Zeitpunkt,
an dem das System die ausgewählte Datei geladen und dargestellt hat, vergeht.
Die Verarbeitungszeit umfasst nur die Zeit, die der Computer mit der Berechnung innerhalb der
Anwendung beschäftigt ist. Netzwerkverzögerungen gehören beispielsweise nicht dazu.
Der Durchsatz ist eine Angabe, die Informationen darüber liefert, wieviele Ereignisse ein System
in einem Zeitintervall abarbeiten kann, z. B. wieviele Transaktionen eine Datenbank pro Sekunde
ausführen kann.
Das Verbrauchsverhalten beschreibt die Menge der zur Ausführung benötigten Ressourcen. Der
benötigte Arbeitsspeicher liegt im Fokus des Verbrauchsverhaltens.
Zeit- und Verbrauchsverhalten sind nicht unabhängig voneinander. Werden mehr Ressourcen be-
nötigt, als zur Verfügung stehen, kann, z. B. durch das daraus entstehende Warten auf Ressourcen,
das Zeitverhalten negativ beeinflusst werden. In der Literatur wird Performance zum Teil aus-
schließlich über die Antwortzeiten und den Durchsatz eines Systems definiert [BCK98, S. 79].
Dort wird die Zeit zwischen einem eingehenden Ereignis und der Antwort bzw. die Anzahl der
Ereignisse die in einem Zeitintervall bearbeitet werden können, als die Performance des Systems
definiert.
Nach dem ISO Standard 9126 [ISO01] ist Performance ein Qualitätsmerkmal eines Software-
systems. Die Qualitätsmerkmale einer Software lassen sich in Kategorien einteilen, insbesondere
unterteilen sie sich in solche, die zur Laufzeit am System messbar sind und solche die es nicht
sind (siehe [BCK98, S. 76]). Portierbarkeit und Änderbarkeit sind Beispiele für nicht zur Laufzeit
messbare Eigenschaften. Performance gehört, ebenso wie z. B. Gebrauchstauglichkeit (Usability),
zu den zur Laufzeit messbaren Eigenschaften. Im Vergleich zu der Gebrauchstauglichkeit ist die
Performance jedoch einfach und direkt messbar, z. B. mittels Profiling und Benchmarking (siehe
2.2.7 und 2.2.6).
Die Definition von Performance bezieht den Benutzer des System zunächst nicht mit ein, dies
leistet der Begriff „wahrgenommene Performance“ [WK00]. Die Benutzer nehmen eine Anwen-
dung, die Fortschrittsinformationen anzeigt, als schneller wahr, als eine die das nicht tut [Shi00,
S. 6]. Wenn man es misst, ist das System mit Fortschrittsinformationen höchstens gleichschnell,
in der Regel minimal langsamer. Auch die Varianz in den Antwortzeiten hat einen großen Einfluss
auf die wahrgenommene Performance. Man kann sie verbessern, indem die besonders lange dau-
ernden Anfragen beschleunigt werden, selbst wenn man dabei die durchschnittliche Antwortzeit1In anderen Werken wird anstelle von Performance der Begriff Effizienz verwendet ([RH06, S. 281], [Lil08, S. 71]).
2.2 Performance 11
erhöht [Shi00, S. 7].
In den folgenden Abschnitten wird Performance unter den zwei Aspekten Zeit und Speicher be-
trachtet. Es gibt weitere Performance-Größen, auf die hier nicht eingegangen wird [Shi00, S. 16].
2.2.1 Zeit
Um den zeitlichen Aspekt von Performance zu messen, braucht man eigentlich nur eine genaue
Stoppuhr. Man startet den zu messenden Vorgang und misst die Zeit, bis er abgeschlossen ist.
Der Begriff Vorgang ist hier bewusst völlig unspezifisch gehalten (siehe Mikro- und Makro-
Benchmarking), da dieses Verfahren keine Einschränkungen vorgibt. Benchmarks (siehe 2.2.6)
und Profilings (siehe 2.2.7) sind zwei Möglichkeiten diese Art Messungen durchzuführen. Alter-
nativ können die Zeiten von der zu messenden Anwendung selber protokolliert werden, indem
z. B. bei jedem Methodenaufruf die aktuelle Zeit protokolliert wird.
Misst man mit einer Stoppuhr die Zeit, die eine Anwendung für die Abarbeitung einer Methode
braucht, dann misst man deren „wall clock time“. Dabei werden weder andere Prozesse noch an-
dere Threads berücksichtigt, die in der Zwischenzeit auf dem Prozessor ausgeführt wurden [Shi00,
S. 16]. Es wird die real verstrichene Zeit festgehalten. In Anwendungen mit mehreren gleichzeitig
laufenden Threads, kann es dazu kommen, dass die Gesamtzeit die gemessen wurde, länger ist als
die Anwendung real lief.
Im Gegensatz dazu steht die Prozessorzeit-Messung (englisch: CPU Time). Bei dieser Variante
wird nur die Zeit gemessen, die die Methode von dem Prozessor ausgeführt wird. Insbesondere
heißt das, dass wenn ein anderer Prozess oder Thread die Ausführung unterbricht, auch die Mes-
sung unterbrochen wird. Der Nachteil dieser Methode ist, dass die meisten Betriebssysteme die
Prozessorzeit nur in grober Genauigkeit von Millisekunden liefern. Eine feinere Genauigkeit wür-
de einen zu großen Mehraufwand bedeuten [ej-08, S. 35].
Die Zeit könnte auch in Takten oder auszuführenden Befehlen des Prozessors angegeben werden.
Dies ist aber für Hochsprachen wie Java unüblich. Die Softwarewerkzeuge liefern die Zeiten in
der Regel in Mikro- oder Nanosekunden.
2.2.2 Speicher
Zur Ausführung benötigt ein Programm eine gewisse Menge an Arbeitsspeicher. Der benötig-
te Speicher ist nicht direkt für den Benutzer wahrnehmbar, aber er ist dennoch ein wichtiges
Performance-Kriterium. Dies trifft insbesondere auf die beiden Aspekte Gesamtspeicher und Spei-
cherlecks zu. Damit eine Anwendung ohne Geschwindigkeitseinbußen auf einem System laufen
kann, muss der für sie zur Verfügung stehende Arbeitsspeicher mindestens so groß sein, wie der
benötigte Speicher. Dabei kann je nach Anwendungsfall entweder der Durchschnitts- oder der Ma-
ximalspeicherbedarf interessant sein. Laufen auf einem Server viele Anwendungen, ist der Durch-
schnittsspeicherbedarf jeder Anwendung eine wichtige Größe. Unter der Annahme, dass nicht alle
Anwendungen gleichzeitig maximal viel Speicher anfordern, kann der Server mit weniger Spei-
cher auskommen. Obwohl Arbeitsspeicher heute vergleichsweise günstig ist, ist er, gerade unter
12 Kapitel 2 Grundlagen
dem Aspekt eine Vielzahl von Anwendungen gleichzeitig auf einem System laufen zu lassen, stets
eher zu klein sein.
Der andere wichtige Aspekt, die Speicherlecks (memory leaks), bezeichnet Fehler in Programmen,
die dazu führen, dass angeforderter Speicher nicht wieder freigegeben wird. In Sprachen, in denen
sich der Programmierer selbst um die Speicherverwaltung kümmern muss (z. B. C++), entstehen
Speicherlecks, wenn der Programmierer vergisst, einen mit new angeforderten Speicherbereich mit
delete wieder frei zu geben. Dies resultiert darin, dass die Anwendung mit zunehmender Laufzeit
immer mehr Speicher benötigt. Steht schließlich kein freier Speicher mehr zur Verfügung, kommt
es zu ungewolltem Verhalten, wie einem Programmabbruch.
In Programmiersprachen mit automatischer Speicherverwaltung (z. B. Java), muss ein angeforder-
ter Speicherbereich nicht explizit wieder freigegeben werden. Die Speicherverwaltung sorgt dafür,
dass ein nicht mehr referenziertes Objekt entfernt wird, da es keinen Einfluss auf den weiteren
Programmverlauf mehr hat. Dennoch kann es in solchen Programmiersprachen zu Speicherlecks
kommen, wenn der Programmierer unbeabsichtigt eine Referenz auf ein Objekt behält. Ein sol-
ches Objekt kann, obgleich es eigentlich nicht mehr benötigt wird, nicht vom Garbage Collector
aufgeräumt werden (siehe 2.2.8).
Profiler (siehe 2.2.7) enthalten Werkzeuge, die beim Analysieren von Speicherlecks helfen kön-
nen. Bevor man ein Speicherleck finden kann, muss man zunächst feststellen, dass eines vorhanden
ist. Für Sprachen mit automatischer Speicherverwaltung bietet sich folgendes Vorgehen an. Das
zu untersuchende Programm wird benutzt und dabei beobachtet, ob mit zunehmender Laufzeit der
Speicherverbrauch immer weiter steigt. Dabei muss dafür gesorgt werden, dass das Programm im-
mer wieder in einen Ausgangszustand zurückgesetzt wird. Steigt dennoch der Speicherverbrauch
immer weiter an, ist dies ein Anzeichen für ein Speicherleck.
Der nächste Schritt besteht darin, die Objekte zu identifizieren, die das Leck verursachen. Da-
für wird das Programm gestartet und in seinen Gebrauchszustand gebracht. In diesem Zustand
lässt man den Garbage Collector laufen. Das lässt sich z. B. mittels Profiler komfortabel erledigen.
Anschließend wird die Anzahl an Objekten dokumentiert. Dies ist der Grundzustand in den man
später wieder zurückkehrt.
Auf diesem Zustand aufbauend arbeitet man weiter mit dem Programm und kehrt nach einiger
Zeit in den Grundzustand zurück. Nachdem der Garbage Collector erneut durchgelaufen ist, ver-
gleicht man die nun vorhandene Anzahl an Objekten mit der, die man zuvor gemessen hatte. Wenn
ein Speicherleck vorliegt, dann sollten mehr Objekte vorhanden sein. Wenn dies nicht der Fall ist,
dann benötigt man mehr oder andere Arbeitsschritte zwischen den zwei Messpunkten.
Ausgehend von den neu hinzugekommenen Objekten, müssen jetzt Referenzen zurückverfolgt
werden, um festzustellen, welche sie davon abhalten vom Garbage Collector aufgeräumt zu wer-
den. Profiler bieten hierfür Werkzeuge an, die es dem Programmierer ermöglichen alle Referen-
zen auf ein bestimmtes Objekt anzuzeigen. So kann man die Pfade rückwärts bis zu den Wurzeln
zurückverfolgen. Für eine detaillierte Beschreibung der Möglichkeiten Speicherlecks in Java zu
identifizieren und zu beheben, siehe z. B. Tate [Tat01].
2.2 Performance 13
2.2.3 Nebenläufigkeit
Die Performance von Systemen hängt auch von dem Grad an Parallelisierung ab. Durch neben-
läufige Ausführung mehrerer Threads bzw. Prozesse, können Pausen die z. B. durch das Warten
auf externe Speicher entstehen würden, überbrückt werden. Das Verwalten der Threads bzw. Pro-
zesse und deren Synchronisation hat allerdings ebenfalls Auswirkungen auf die Performance. Im
schlimmsten Fall kommt es zu „Deadlocks“, also Situationen in denen mehrere Threads so auf-
einander warten, dass keiner mehr weiterarbeiten kann. Auch Nebenläufigkeitsaspekte lassen sich
mittels Profiler untersuchen, z. B. können die unterschiedlichen Threads und ihre Synchronisati-
onspunkte aufbereitet und dargestellt werden, so dass deutlich wird, welcher Thread aus welchem
Grund aktuell nicht ausgeführt werden kann.
In dieser Arbeit werden Nebenläufigkeitsproblematiken nicht weiter behandelt.
2.2.4 Gute Performance
Mit zunehmendem Verständnis des Begriffs Performance, stellt sich die Frage, wann das Qualitäts-
merkmal Performance erfüllt ist und falls es das nicht ist, wie man die Performance-Eigenschaften
verbessern kann. Performance ist ein wichtiges Kriterium welches Benutzer an Software anlegen
[Shi00, S. 10]. Die Gebrauchstauglichkeit von Software hängt u. a. von der Performance ab (siehe
2.2). In einigen Szenarien ist die Performance ausschlaggebend für die Konkurrenzfähigkeit ei-
ner Software. Performance-Probleme sind eine Ursache für das Scheitern von Softwareprojekten
[Gla97].
Das Verbessern der Performance-Eigenschaften eines Systems ist demnach eine wichtige Tätig-
keit, die jedoch besonders stark strukturiert und eingegrenzt werden muss. Optimieren (siehe 2.2.5)
sollte man stets auf ein Ziel hin. Ein solches Ziel könnte sein, eine bestimmte Anzahl von Bildern
pro Sekunde zu rendern, eine bestimmte durchschnittliche Antwortzeit zu erreichen oder nur eine
bestimmte Menge an Speicher zu benötigen. Hat man kein Ziel formuliert, dann merkt man nicht,
wann man eigentlich aufhören sollte. Generell gilt: Man sollte nicht optimieren, wenn nicht op-
timiert werden muss [Shi00, S. 17]. Optimierung führt zu schlechter lesbarem Quelltext. Selbst
wenn dies in einer gegebenen Situation nicht zutrifft, hätte man in der gleichen Zeit neue Funktio-
nalität entwickeln oder Fehler ausbessern können.
2.2.5 Optimierung
Der Begriff Optimierung bezeichnet den Prozess etwas zu verbessern, also dichter an das Op-
timum heranzubringen. Dementsprechend bezeichnet der Begriff Performance-Optimierung den
Prozess, die Performance zu verbessern. Architektur-Optimierung bezeichnet das Verbessern der
Architektur.
In dieser Arbeit schreibe ich kurz Optimierung, wenn ich Performance-Optimierung meine. Wenn
es um eine andere Art von Optimierung geht, wird dies explizit erwähnt. In anderen Arbeiten wird
statt optimieren auch der Begriff „tunen“ verwendet.
In Bezug auf Software bedeutet optimieren das Transformieren eines Quelltextes in einen neuen
14 Kapitel 2 Grundlagen
Quelltext. Diese Transformation soll dabei die Korrektheit erhalten und die resultierende Anwen-
dung performanter machen [Bul00, S. xxiii].
Um Struktur in den Prozess der Optimierung zu bringen, wird das Vorgehen mit Hilfe von einer
allgemeinen Strategie und einem Verfahren definiert. Die Strategie sieht vor, dass man immer wie-
der die fünf schwerwiegendsten Flaschenhälse (bottleneck, Hotspots) identifiziert und davon den
korrigiert, der am einfachsten und schnellsten zu korrigieren ist. Danach misst man neu und fängt
von vorne an [Shi00, S. 5].
Es ist wichtig, nicht mit dem schwerwiegendsten Flaschenhals anzufangen, weil sich durch das
Korrigieren des einfachsten schon eine völlig andere Situation ergeben kann. Dies liegt daran,
dass die Flaschenhälse oft nicht unabhängig voneinander sind, sondern Auswirkungen aufeinan-
der haben. Es lohnt sich dementsprechend nicht, viel Zeit in eine Optimierung zu stecken, wenn
diese durch eine andere, schneller durchzuführende Optimierung unnötig gemacht würde.
Typischerweise erhält man durch die ersten Optimierungen Geschwindigkeitsvorteile von einer
Größenordnung, bereits nach einigen Optimierungen nimmt dies stark ab [Shi00, S. 3]. Nach
Bulka gilt auch für Optimierungen die 80-20-Regel, die in diesem Kontext besagt, dass 20 Pro-
zent der Optimierungen 80 Prozent der Performanceverbesserungen bewirken [Bul00, s. xxiv].
Nach Möglichkeit sollte vermieden werden, die anderen 80 Prozent an Optimierungen durchzu-
führen, weil sie den Aufwand und die resultierende Verschlechterung des Quelltextes nicht Wert
sind (s. u.).
Das Verfahren, dem beim Optimieren gefolgt wird, stammt von Shirazi [Shi00, S. 5] und besteht
aus folgenden Schritten:
1. Mittels Profiler die Performance messen (siehe 2.2.7).
2. Flaschenhälse identifizieren und lokalisieren.
3. Eine Hypothese für die Ursache aufstellen.
4. Alle Faktoren in Betracht ziehen, die die Hypothese widerlegen könnten.
5. Einen Test schreiben, der das Problem (Hypothese) isoliert.
6. Die Hypothese überprüfen.
7. Die Anwendung anpassen.
8. Durch Profiling überprüfen, dass die Performance verbessert wurde (und mit Komponen-
tentests sicherstellen, dass sich keine Fehler eingeschlichen haben).
9. Wieder bei Schritt eins anfangen.
Schritt 5 und 7 passieren häufig in Kombination, allerdings kann das den Optimierungsprozess
verlängern, wenn sich erst danach herausstellt, dass die Hypothese falsch war.
Folgende Ressourcen begrenzen typischerweise die Performance: Geschwindigkeit und Verfüg-
barkeit des Prozessors, der Speicher und externe Datenquellen/-senken wie Festplatten oder Netz-
werke. Es sollte stets betrachtet werden, welcher dieser drei Faktoren die Geschwindigkeit in einer
2.2 Performance 15
gegebenen Situation begrenzt.
Auf Methodenebene gibt es generell zwei Möglichkeiten bezüglich der Ausführungszeit zu op-
timieren: Entweder oft genutzte Methoden schneller machen oder langsame Methoden weniger
häufig aufrufen. Dies kann z. B. erreicht werden, indem Ergebnisse zwischengespeichert werden.
Die zweite Möglichkeit wird häufig übersehen [WK00].
Eine weitere Möglichkeit die Anwendung zu beschleunigen besteht darin, nicht gebrauchte Funk-
tionalität zu entfernen. Wenn die Anforderungen noch unklar sind, werden Methoden häufig zu
generisch geschrieben. Im Laufe der Entwicklung können solche Methoden dann spezifischer ge-
macht werden. Dies erhöht neben der Performance wahrscheinlich die Les- und Benutzbarkeit,
z. B. weil die Parametertypen spezifischer sind. Optimierter Quelltext ist also weniger generisch
und damit schlechter wiederzuverwenden, weil er mehr an die Anwendungsdomäne angepasst ist
(siehe [Bul00, S. xxiii]).
Das oben beschriebene Verfahren beinhaltet u. a., dass nach jeder Änderung ein Profiling durch-
geführt wird. Dies ist aus zwei Gründen wichtig. Erstens ist nicht klar, ob eine vermeintliche
Verbesserung tatsächlich messbare Vorteile bringt. Zweitens kommt es vor, dass durch die Opti-
mierung einige Funktionen beschleunigt, aber andere verlangsamt werden [Shi00, S. 14]. Es gibt
Situationen in denen das Weglassen von Quelltext in einer Schleife die Performance der Schleife
verschlechtert hat [Ben99]. Dies zeigt, es gibt keine Veränderungen die offensichtlich die Perfor-
mance verbessern. Wenn eine Optimierung sich nicht durch eine Messung bestätigen lässt, dann
sollte sie rückgängig gemacht werden.
Neben dem Profiling müssen auch die Komponententests nach jeder Anpassung ausgeführt wer-
den. Durch die Optimierung schleichen sich Fehler ein [Shi00, S. 15]. Dies liegt daran, dass der
optimierte Quelltext zum Teil schwieriger zu lesen ist [Bul00, S. xxiii]. Deshalb sollte stets ein
Kommentar im Quelltext darauf hinweisen, dass der Quelltext aufgrund von Optimierungen ange-
passt wurde.
Anwendungen wie z. B. Web-Anwendungen, die von mehreren Benutzern gleichzeitig benutzt
werden, sollten zunächst im Ein-Benutzer-Betrieb untersucht werden. Wenn möglich sollten aber
bereits früh in der Entwicklungsphase Tests mit mehreren Benutzern gleichzeitig durchgeführt
werden, da nur so konzeptionelle Probleme entdeckt werden können, die ein Redesign erfordern
(siehe auch 2.2.3).
Bei explizit für Wiederverwendung entwickeltem Quelltext (z. B. Rahmenwerke, Bibliotheken) ist
Optimierung nur mit großer Vorsicht durchzuführen. Es ist dort typischerweise nicht möglich, alle
Anwendungsmöglichkeiten in passenden Tests zu simulieren. Dies macht es schwierig sicherzu-
stellen, dass eine vermeintliche Optimierung die Performance nicht in anderen Anwendungsfällen
verschlechtert [Shi00, S. 358].
2.2.6 Benchmarking
Benchmarking ist neben Profiling eine Möglichkeit die Performance einer Anwendung zu messen.
Beim Benchmarking wird eine quantitative Größe bestimmt, die einen speziellen Aspekt einer An-
16 Kapitel 2 Grundlagen
wendung kennzeichnet [WK00]. Typischerweise ist diese Größe die Zeit. Dabei kann ein Bench-
mark unterschiedlich große Programmabschnitte messen. Der Bereich reicht von einer einzelnen
Methode (Mikro-Benchmark) bis zu mehreren Use-Cases (Makro-Benchmark). Benchmarking ist
nur dann von Nutzen, wenn man verschiedene Benchmarks miteinander vergleicht. Beispielswei-
se eignet sich Benchmarking, um das Verhalten von zwei unterschiedlichen Sortieralgorithmen in
unterschiedlichen Testfällen gegenüber zu stellen.
Benchmarks können Profiling gut unterstützen. Da sie wiederholbar sein sollen, enthalten sie klar
definierte Programmabläufe. Diese können als wiederholbare Testfälle für das Profiling sinnvoll
sein. Ohne einen solchen Testfall lässt sich eine Änderung, die auf Grund eines Profilings durch-
geführt wurde, schlecht auf ihre Wirksamkeit überprüfen. Es müsste von Hand versucht werden,
den gleichen Programmablauf zu erzeugen. Dies wäre fehleranfällig und zeitaufwendig. Steht mit
einem Benchmark ein definierter Programmablauf zur Verfügung, muss dieser nur erneut angesto-
ßen werden.
Ein dritter Grund, warum Benchmarks sinnvoll sein können, ist, dass sie bereits während der Ent-
wicklungsphase Feedback geben können, wie Änderungen sich auf die Performance auswirken.
Wenn sie während der Entwicklung regelmäßig ausgeführt werden, liefern sie einen ersten Indi-
kator dafür, ob sich die Performance der Anwendung verbessert oder verschlechtert.
Mikro-Benchmarks sind kleine Benchmarks, mit denen kurze Programmabschnitte, wie einzelne
oder wenige Methoden, gemessen werden. Sie können z. B. eingesetzt werden, um zu evaluieren,
welcher Algorithmus in der gegebenen Situation passender ist. Sie stoßen jedoch schnell an ih-
re Grenzen: Wenn z. B. eine moderne HotSpot Java Virtual Machine (JVM) eine Methode erst
bei mehrmaligem Aufruf kompiliert (anstatt sie interpretiert auszuführen), können die Ergebnisse
stark von den im realen Umfeld beobachteten abweichen. Des Weiteren werden die Wechselwir-
kungen mit anderen Teilen des Programms vernachlässigt.
Makro-Benchmarks messen das System auf der gleichen Ebene, wie die Benutzer es verwenden.
Ein oder mehrere Use-Cases können als Grundlage dafür verwendet werden. Es wird versucht mit
Daten zu arbeiten, die den vom Benutzer später eingesetzten Daten entsprechen. Dadurch, dass das
System als Ganzes gestartet und mitgemessen wird, werden Effekte sichtbar, die im Zusammen-
spiel unterschiedlicher Programmteile entstehen. Idealerweise wird ein Makro-Benchmark auf der
Zielumgebung ausgeführt, für die die Anwendung entwickelt wird.
Um die Ergebnisse eines Benchmarks verlässlicher zu machen, werden Benchmarks mehrmals
hintereinander ausgeführt und die entsprechenden Ergebnisse als Ganzes ausgewertet. Damit kön-
nen Ausreißer erkannt und entsprechend behandelt werden. Ausreißer können z. B. durch andere
Prozesse, die im Hintergrund laufen, entstehen. Beim mehrmaligen Ausführen muss beachtet wer-
den, inwieweit durch Caching oder ähnliche Mechanismen die Ausführungszeit späterer Durch-
läufe von den vorherigen abhängt. Insbesondere wenn Java verwendet wird, muss entschieden
werden, ob die gleiche JVM verwendet wird oder nicht. Wenn davon auszugehen ist, dass der
im Benchmark betrachtete Fall in der realen Verwendung nur selten vorkommt, kann es sinnvoll
sein, eine neue JVM zu starten, um zu verhindern, dass mehr als in der realen Anwendungssitua-
2.2 Performance 17
tion optimiert wird. Andererseits kann es, gerade für häufig wieder auftretende Anwendungsfälle,
sinnvoll sein, die gleiche JVM zu verwenden, damit die Warm-Up-Phasen weniger ins Gewicht
fallen [WK00]. Die Warm-Up-Phase ist die Zeit, in der die Anwendung noch nicht ihre volle
Leistungsfähigkeit erreicht hat. Dies liegt daran, dass die JVM noch nicht ausreichend Statisti-
ken hat, um zu entscheiden, welche Methoden in nativen Code kompiliert werden sollen (siehe
auch 2.2.8). Beispielsweise kann davon ausgegangen werden, dass ein Web-Server viele Seiten
ausliefert, nachdem er einmal gestartet wurde. Es müssen nicht jedes Mal die benötigten Klassen
geladen werden und die HotSpot JVM hat bereits die häufig aufgerufenen Methoden in nativen
Code kompiliert. Um dem zu entsprechen, sollte dann die JVM nicht neu geladen werden.
2.2.7 Profiling
Im Gegensatz zu Benchmarking wird beim Profiling nicht nach der Gesamtzeit für einen Anwen-
dungsfall gesucht, sondern es wird versucht „performance bottlenecks“ (Flaschenhälse) zu finden.
Diese Hotspot genannten Programmabschnitte, sind dadurch charakterisiert, dass sie einen großen
Teil der Ausführungszeit in Anspruch nehmen.
Profilings werden mit Software-Werkzeugen durchgeführt die als Profiler bezeichnet werden. Mit
diesen kann u. a. festgestellt werden, welche Methoden wie oft aufgerufen wurden, welche Me-
thoden wieviel Prozent der Zeit benötigen und welche Methoden wieviel Speicher anfordern.
Der Profiler liefert die Information, wieviel Zeit eine Methode zur Ausführung braucht. Dabei wird
zwischen der nicht-kumulativen und der kumulativen (geerbten) Zeit unterschieden. Als nicht-
kumulative Zeit, bezeichnet man die Zeit, die das Programm in genau dieser Methode verbringt.
Die Zeit, die es in daraus aufgerufenen Methoden verbringt, wird nicht mit gemessen. Mit Hilfe
der so gewonnenen Zeiten, lassen sich schnell Methoden identifizieren, bei denen sich das Opti-
mieren lohnt.
Wenn es keine Methode mehr gibt, die signifikant mehr Zeit benötigt als die anderen, dann spricht
man von einem flachen Profil [WK00]. Ist die Performance dennoch nicht ausreichend, dann kann
es hilfreich sein, sich die kumulativen Zeiten anzuschauen. Auf diese Weise betrachtet man nicht
nur die Zeit, die das Programm direkt in der Methode ist, sondern auch die Zeit, die es in allen
Methoden verbringt, die aus ihr heraus aufgerufen werden.
Im Folgenden wird Profiling unter Java näher beschrieben. In den Java Versionen vor Java 5 gab
es keine standardisierte Schnittstelle zum Profiling von Java-Anwendungen. Es gab zwar das Java
Virtual Machine Profiler Interface (JVMPI), welches von einigen JVMs implementiert wurde, al-
lerdings konnte sich dieses von Version zu Version ändern. Seit Java 5 gibt es mit dem Java Virtual
Machine Tool Interface (JVMTI) eine standardisierte Schnittstelle, die diesen Missstand behebt
und auf allen JVM Implementierungen zur Verfügung stehen muss. Anhand von diesem Interface
kann ein Profiler sich von der JVM über eine Reihe von Ereignissen informieren lassen. Dazu
gehören das Starten/Beenden der JVM, Ereignisse bezüglich dem Laden von Klassen, bezüglich
dem Lebenszyklus von Threads und bezüglich dem Lebenszyklus von Objekten und dem Garbage
Collector (siehe auch [ej-08, S. 9 ff.]).
18 Kapitel 2 Grundlagen
Das JVMTI kann auf unterschiedliche Arten benutzt werden, um die Zeiten zu erhalten, die für
die Ausführung einer Methode gebraucht wird. Die Laufzeiten einzelner Methoden können über
Sampling angenähert werden. In kurzen Abständen wird der Aufrufstack des Programms erfragt.
Ist eine Methode bei 100 Aufrufen 10 mal ganz oben auf dem Stack (befindet sich also in der Aus-
führung), dann wird davon ausgegangen, dass sie 10 Prozent der Laufzeit benötigt. Diese Technik
ist nicht sicher oder gar exakt, aber für genügend lange Zeiträume (Sekunden, nicht Millisekun-
den) und entsprechend gewählte Sampling-Raten ist sie brauchbar.
Eine Alternative zum Sampling ist Dynamic Instrumentation [BHM07]. Das Grundprinzip hierbei
ist, dass das Programm vor der Ausführung verändert wird und die Methodenaufrufe so mitproto-
kolliert werden können. In Java ist es möglich, dies dynamisch zu tun, wenn eine Klasse von der
JVM geladen wird. Dafür wird der Bytecode der Klasse dem Profiler zur Verfügung gestellt. Der
Bytecode wird dann vom Profiler so manipuliert, dass z. B. am Anfang und Ende jeder Methode
der Profiler informiert wird. Die manipulierte Klasse wird dann von der JVM geladen und normal
in der Anwendung verwendet. Dies ermöglicht ein genaues Erfassen der Ausführungszeiten und
darüber hinausgehender Informationen. Nachteil dieses Verfahrens ist, dass ein großer Mehrauf-
wand (Overhead) entsteht, wenn in allen Methoden von allen Klassen die Aufrufe protokolliert
werden [Dmi04]. Dynamic Instrumentation begegnet dem Problem, indem nur noch die Klassen,
die der Benutzer wünscht, verändert und damit gemessen werden.
Von Full Instrumentation spricht man, wenn die JVM angewiesen wird, jeden Methodenaufruf
dem Profiler mitzuteilen. Dies produziert einen sehr großen Mehraufwand. Der Vorteil ist, dass
alle Klassen gemessen werden können. Beim Dynamic Instrumentation können nicht alle Klassen
gemessen werden, insbesondere solche nicht, die schon beim Start der JVM geladen werden. Dies
betrifft z. B. solche aus den Packages java.* und sun.* [ej-08, S. 15 f.].
2.2.8 Performance-Modell in Java
Die Entwurfsziele von Java waren u. a. Portabilität und Sicherheit. Der Aspekt Performance wur-
de nie als Hauptziel angesehen. Eine Folge daraus ist, dass es kein richtiges Performance-Modell
für Java gibt. Ein solches wäre nötig, damit verbindliche Aussagen über Performance-Aspekte ge-
macht werden können. Beispielsweise werden keine Aussagen darüber gemacht, ob, und wenn
ja, wie mehrere Threads auf einem Mehrprozessorsystem auf die Prozessoren verteilt werden
[Lea98]. Die Schwierigkeit ein Performance-Modell aufzustellen ist u. a. darin begründet, dass
es viele unterschiedliche Implementierungen der Java Virtual Machine für viele unterschiedliche
Systeme gibt. Dies macht es umso wichtiger, alle Optimierungen auf der Zielplattform mit der
dort verwendeten JVM zu testen. Die Situation wird noch komplizierter, da die JVM eigenständig
und dynamisch optimiert. Dies wird im Folgenden vorgestellt.
In dieser Arbeit beziehe ich mich, falls nicht anders angegeben, auf Java ab Version 5. Seit Java
Version 1.3 setzt Sun Microsystems2 bei den JVM auf die HotSpot-Architektur und nennt die JVM
auch so. Eine HotSpot-JVM besteht aus zwei Komponenten, der Runtime und dem Compiler. Die
2Sun Microsystems hat Java entwickelt und ist einer der führenden JVM Entwickler.
2.2 Performance 19
Abbildung 2.1: Aufbau der Sun HotSpot JVM [Sun99]
Runtime enthält einen Java-Bytecode Interpreter und liefert das Speichermanagment und Mecha-
nismen zur Threadsynchronisation. Sie alleine ist eine voll funktionsfähige JVM. Zusätzlich wird
mit dem Compiler eine weitere Komponente zur Verfügung gestellt (siehe Abbildung 2.1). Dieser
Compiler kompiliert zur Laufzeit der Anwendung Java-Bytecode in nativen Maschinencode. Diese
Art von Compiler werden Just-In-Time-Compiler (JIT-Compiler) genannt. Er ist nicht zu verwech-
seln mit javac, der den Java-Quelltext in Java-Bytecode kompiliert. Die Compiler-Komponente
der HotSpot-JVM unterteilt sich wiederum in zwei Varianten. Die Client-Variante enthält einen
simplen Compiler, welcher kaum Optimierungen durchführt. Dieser Compiler braucht weniger
zusätzlichen Speicher und verzögert die Startzeit der Anwendung nicht so stark. Unter Startzeit
ist hier die Zeit gemeint, die der Benutzer warten muss, bis er die Anwendung benutzen kann. Die
Server-Variante enthält einen stark optimierenden Compiler, der die Länge der Warm-Up-Phase
beeinflusst. So dauert es mit der Server-Variante in der Regel deutlich länger, bis die maximale
Performance erreicht ist, diese ist dann aber besser als die der Client-Variante.
Für beide Varianten gilt, dass sie nicht jede Methode kompilieren. Eine Vielzahl an Methoden
werden nur selten aufgerufen. Für diese würde die Gesamtperformance verschlechtert, wenn sie
erst kompiliert und dann ausgeführt würden. Sie werden weiterhin interpretiert. Wenn die Anzahl
der Aufrufe einer Methode einen Grenzwert überschreitet, wird diese Methode kompiliert. Diese
Methoden stellen Hotspots dar (siehe 2.2.7) und sind namensgebend für die JVM. Der Grenz-
wert lässt sich über nicht-standardisierte Parameter der JVM anpassen. Der voreingestellte Wert
ist 1500 für die Client- bzw. 10000 für die Server-Variante [WK00].
Ein wesentlicher Bestandteil der Runtime-Komponente ist der Garbage Collector (GC). Aufgabe
des GC ist es, nicht mehr benötigte Objekte zu finden und zu zerstören, um den belegten Speicher
wieder frei zu geben. Objekte sind dann zu entfernen, wenn sie für die weitere Ausführung des
Programms keine Bedeutung mehr haben. Dies ist aus Sicht des GC dann erreicht, wenn es von
den Wurzelknoten und den lokalen Variablen keine Referenz mehr auf ein Objekt gibt. Auf ein
solches Objekt kann im weiteren Programmverlauf nicht mehr zugegriffen werden. Es erfüllt des-
wegen die Bedingung, dass es keinen Einfluss mehr auf den Programmverlauf hat.
Der GC-Algorithmus hat im Laufe der Entwicklung einige Verbesserungen erfahren. Ein wesent-
liches Konzept ist das Generationen-Konzept. Dem zu Grunde liegt die Beobachtung, dass es viele
kurzlebige Objekte und wenige langlebige Objekte gibt. Die Generation der kurzlebigen Objekte
wird häufiger vom GC durchsucht und erst wenn ein Objekt darin mehrere GC-Zyklen überlebt
20 Kapitel 2 Grundlagen
hat, wandert es in die Generation der langlebigen Objekte. Verfeinert wird dies durch den so ge-
nannten Train-Algorithmus. Dieser ermöglicht es, die Länge der Pausen, die durch einen GC-Lauf
ausgelöst werden, kurz zu halten und sorgt gleichzeitig dafür, dass zusammenhängende Objekte
im Speicher dicht beieinander liegen. Dies hat insbesondere für Paging und Caching Vorteile. De-
taillierte Informationen zum Garbage Collector und den verwendeten Algorithmen können z. B.
bei Venners [Ven96, Kapitel 9] oder Wilson und Kesselman [WK00] gefunden werden.
2.3 JCommSy
In diesem Abschnitt wird das JCommSy vorgestellt. Dafür wird zunächst eingeführt, was ei-
ne Web-Anwendung ist und anschließend skizziert, was das CommSy ist. Dies führt dann zum
JCommSy.
2.3.1 Web-Anwendung
Das World Wide Web (WWW) ist ein Dienst, der über das Internet angeboten wird. Per Hypertext
wurden anfangs hauptsächlich statische Inhalt miteinander verknüpft und zugänglich gemacht.
Die Struktur des WWW sieht eine Client-Server-Architektur vor: Webbrowser (die Clients) for-
dern Inhalte von Servern an und stellen diese dem Benutzer dar. Mit der Zeit wurden sowohl die
Clientseite als auch die Serverseite weiterentwickelt, so dass heute auf beiden Seiten dynamisch
Inhalte verarbeitet/dargestellt werden können (siehe auch [TSL04, S. 1]). Eine Web-Anwendung
ist definiert als eine Anwendung, die auf einem Server läuft und dort dynamisch Dokumente er-
zeugt und bereitstellt, welche über das Hypertext Transfer Protocol (HTTP) von einem Browser
aus abgerufen und von diesem dargestellt werden. In diese Kategorie fallen beispielsweise Inter-
netshops und das CommSy.
2.3.2 CommSy
Das CommSy3 ist eine an der Universität Hamburg entwickelte Web-Anwendung. Das Projekt
ist 1999 gestartet worden und hat sich seit dem stark weiterentwickelt. Es ist eine „Plattform für
universitäre Lerngemeinschaften“ [CT05, S. 6], in der man zusammen lernen, sowie Projektarbeit
organisieren und unterstützen kann. Insbesondere das projektbasierte Lernen (siehe [JB02]) und
eine gute Benutzbarkeit sind im Fokus der Entwicklung. Über die ursprünglichen Ziele hinausge-
hend, wird das CommSy heute in Schulen und Unternehmen eingesetzt [Sch08b, S. 54].
Das CommSy ist in Räume strukturiert, für die man Mitgliedschaften beantragen kann. Dies er-
möglicht es, geschlossene Lerneinheiten zu erzeugen. Es kann z. B. für jedes Seminar eines Stu-
diengangs einen Raum geben.
In den Räumen werden unterschiedliche Funktionalitäten angeboten. Sie werden Rubriken ge-
nannt. Beispiele für Rubriken sind Ankündigungen, Materialien und Termine. In der Termine-
Rubrik kann z. B. ein Termin mit dem Datum eines Vortrags in einem Seminar angelegt werden.
3http://www.commsy.net/
2.3 JCommSy 21
Die Rubriken sind nicht unabhängig voneinander, z. B. kann einem Termin ein Material zugeord-
net werden.
2.3.3 Migrationsprojekt JCommSy
Das CommSy ist in der Skriptsprache PHP geschrieben. Skriptsprachen sind aufgrund ihrer ein-
fachen Erlernbarkeit und guten Unterstützung durch existierende Bibliotheken gerade für Web-
Anwendungen beliebt [Jee05]. Der Nachteil von Skriptsprachen ist besonders in der inneren Qua-
lität der Software zu finden. Im Gegensatz zu äußeren Qualitäten, wie der Performance oder der
Benutzbarkeit, zählen zu den inneren Qualitäten solche, die für den Benutzer nicht wahrnehmbar
sind (siehe auch 2.2). Da die Skriptsprachen keine guten Strukturierungsmöglichkeiten besitzen,
leidet die Lesbarkeit des Quelltextes. Konzepte wie ein Typsystem, das Geheimnisprinzip [Par72]
oder das Vertragsmodell [Mey92] werden nicht unterstützt. Dies macht den Quelltext, gerade bei
wachsenden Systemen, unübersichtlich, fehleranfällig und schwer wartbar [Jee05]. Das trifft umso
mehr zu, wenn die Entwickler noch unerfahren sind, wie z. B. Studierende im Fall des CommSy.
Gerade für Anfänger ist es wichtig, dass es Strukturen und Regeln gibt, die sie anleiten und deren
Verletzungen ggf. automatisiert festgestellt werden können [BPKL06, S. 35].
Um das wachsende CommSy wartbar zu halten, wurde entschieden von PHP auf Java umzustei-
gen, d. h. zu migrieren. Dieses Migrationsprojekt trägt den Namen JCommSy. Ziel ist es, die glei-
che Funktionalität wie das PHP-CommSy anzubieten. Das Vorgehen bei der Migration erfolgt
dabei schrittweise und vertikal (siehe [Jee05]). Es wurde mit einer Rubrik angefangen und die-
se durch alle Schichten hindurch (also Präsentation, Logik, bis zum Datenbankzugriff) auf Java
umgestellt. Sowohl im PHP-CommSy, als auch im JCommSy wurde eine Weiche eingebaut, die
sicherstellt, dass Rubriken, die noch nicht im JCommSy umgesetzt sind, vom PHP-CommSy be-
arbeitet werden.
Im Folgenden bezeichnet PCommSy das in PHP geschriebene CommSy. Der Begriff CommSy
wird nur verwendet, wenn sowohl das in Java als auch das in PHP geschriebene CommSy gemeint
ist.
2.3.4 Performance als Kriterium
Die Entwickler des JCommSy haben, zusammen mit den Entwicklern des PCommSy, eine Reihe
von Kriterien festgelegt, die das JCommSy erfüllen soll. Ein Kriterium ist, dass die Oberfläche,
also das Resultat im Browser des Benutzers, gleich aussehen soll wie die des PCommSy. Des
Weiteren soll die Performance, insbesondere die Zeit, die der Server braucht, um eine Anfrage zu
beantworten, mindestens gleich gut sein, wie die des PCommSy. Um diese Kriterien im Auge zu
behalten, wurden bereits frühzeitig Testfälle definiert, die die Anforderungen regelmäßig überprü-
fen. Diese Tests benutzen das Test-Rahmenwerk HttpUnit4. Mit diesem kann der Web-Server an
seiner Oberfläche getestet werden. Es werden HTTP-Anfragen generiert und entsprechend das in
4siehe http://httpunit.sourceforge.net/
22 Kapitel 2 Grundlagen
der Antwort erhaltene HTML-Dokument5 ausgewertet.
2.4 Eingesetzte Technologien
In diesem Abschnitt werden relevante Technologien vorgestellt, die im JCommSy eingesetzt und
im Laufe dieser Arbeit verwendet werden. Es wird jeweils eine Einführung mit Verweisen auf
weiterführende Quellen gegeben.
2.4.1 JavaBeans
Eine JavaBean (oder kurz Bean) ist eine in Java geschriebene wiederverwendbare Softwarekompo-
nente. Ursprünglich wurde dieses Komponentenmodell definiert, um Oberflächenerstellungswerk-
zeugen (GUI-Builder) zu ermöglichen, Komponenten wie Textfelder zu verwenden, ohne dass die-
se bereits vorher bekannt sind. JavaBeans müssen übertragbar und damit serialisierbar sein. Damit
z. B. ein GUI-Builder ein Exemplar einer Bean erzeugen kann, schreibt die Spezifikation einen
Standardkonstruktor vor, also einen Konstruktor ohne Parameter. Eine Bean besitzt Eigenschaf-
ten, die über Getter- und Setter-Methoden zugänglich sein müssen. Die Java-Standardbibliothek
enthält eine Reihe von Hilfsklassen, wie z. B. BeanDescriptor, mit dem Informationen über die
Komponente verfügbar gemacht werden können. JavaBeans sind von Sun Microsystems spezifi-
ziert [Ham97].
2.4.2 Servlet
Die Servlet-Programmierschnittstelle (Servlet-API) ist ein wesentlicher Bestandteil von der Ja-
va 2 Plattform Enterprise Edition (J2EE). Sie erlaubt es, auf hohem Abstraktionsniveau Web-
Anwendungen (siehe 2.3.1) zu entwickeln. Dafür werden eine Reihe von Schnittstellen und Klas-
sen angeboten, so dass sich ein Anwendungsentwickler nicht mit dem Parsen und Dekodieren der
HTTP-Anfrage beschäftigen muss. Um ein Servlet zu schreiben, muss der Entwickler im wesent-
lichen nur die Methode doGet implementieren. Diese wird aufgerufen, wenn eine HTTP-GET-
Anfrage eintrifft. Die Methode erhält ein HttpServletRequest- und ein HttpServletResponse-
Objekt als Parameter, welche die Anfrage und die zu erstellende Antwort darstellen. Das Servlet
wird in einen Servlet-Container, wie z. B. dem Apache Tomcat, geladen und von diesem verwaltet.
Dies umfasst das Starten oder Aufrufen des Servlets, wenn eine Anfrage ankommt.
Ein Nachteil, den die ausschließliche Verwendung von Servlets mit sich bringt, ist, dass die
HTML-Tags innerhalb des Servlets in die HTTP-Antwort geschrieben werden müssen. Damit
sind Präsentationsdetails mit der Anwendungslogik vermengt und es ist z. B. nicht einfach mög-
lich, einen Web-Designer die Gestaltung der Oberfläche ändern zu lassen. Dieser müsste dafür das
Servlet bearbeiten, sich also mit der Programmiersprache Java auskennen.
5Hypertext Markup Language (HTML) ist eine Auszeichnungssprache zum Strukturieren von Dokumenten.
2.4 Eingesetzte Technologien 23
2.4.3 Java Server Pages
Java Server Pages (im Folgenden mit JSP abgekürzt) sind eine Erweiterung der Servlets. Vor dem
ersten Aufruf werden die JSPs vom Servlet-Container in Servlets übersetzt. Sie wurden einge-
führt, um die Darstellungsdetails von der Anwendungslogik trennen zu können und sie z. B. Web-
Designern zugänglich zu machen. JSPs sind in einer HTML ähnlichen Syntax verfasst, erweitern
diese aber um einige Aspekte, so dass in ihnen Java-Quelltext geschrieben werden kann. Die JSPs
sollten als Template-Sprache verstanden werden und ausschließlich für die Darstellung zuständig
sein. Sie sollten z. B. keine Objekte erzeugen und keine Anwendungslogik enthalten, sondern nur
aus übergebenen Objekten die darzustellenden Werte auslesen.
Servlets und JSPs lassen sich sinnvoll gemeinsam verwenden, indem man Servlets für die An-
nahme von Anfragen zuständig sein lässt und aus ihnen heraus die Anwendungslogik aufruft.
Abschließend wird die entsprechende JSP zur Darstellung ausgewählt, welche die Antwortsei-
te generiert. Dies ist eine Implementierung des Model-View-Controller-Musters (MVC) [And03,
S. 52], [ZLMG08, S. 215 ff.]. Im Bereich von Web-Anwendungen wird dieses noch in unterschied-
liche Modelle aufgeteilt. Das Modell 2, welches hier angewendet wird, zeichnet sich dadurch aus,
dass es ein zentrales Einstiegs-Servlet gibt, welches die Veränderung des Modells anstößt und
anschließend an die passende JSP weiterleitet [TSL04, S. 84 ff.].
2.4.4 Tags
Um die JSPs in ihren Möglichkeiten bezüglich der Darstellung nicht einzuschränken, gibt es die
Tag-Bibliotheken. Die JSP Standard Tag Library (JSTL) stellt Kontrollstrukturen wie if oder
forEach in JSP-Syntax für die Verwendung in den JSPs zur Verfügung. Damit kann verhindert
werden, dass in den JSPs für die Darstellungslogik Java-Quelltext geschrieben werden muss. Ne-
ben der vordefinierten JSP Standard Tag Library können eigene Tags definiert werden. Sie können
in JSP-Syntax oder in Java geschrieben werden.
Für weitere Details zu Tags sowie zu Servlets (2.4.2) und JSP(2.4.3) wird auf Kurniawan [Kur02],
Turau [Tur00] sowie Turau, Saleck und Lenz [TSL04] verwiesen.
2.4.5 Hibernate
Moderne Anwendungsentwicklung folgt dem objektorientierten Programmierparadigma [SM02].
Für die Persistenz der Datenbestände werden oft relationale Datenbanken eingesetzt [ZR00]. Um
eine Brücke über den so genannten „impedance mismatch“ zwischen der Objektorientierung und
den relationalen Datenbanken zu schlagen, gibt es objektrelationale Mapper (OR-Mapper). Ein
OR-Mapper leistet die Abbildung von Objekten, wie sie in Java verwendet werden, auf ein rela-
tionales Modell und andersherum. Hibernate6 ist ein solcher OR-Mapper. Darüber hinausgehend,
kann Hibernate den Entwickler davon befreien, Abfragen in der Standard Query Language (SQL)
formulieren zu müssen. Mit der Criteria Query API existiert eine objektorientierte Schnittstelle
um Objekte aus der Datenbank abzufragen (siehe [BHRS07]).6http://www.hibernate.org/
24 Kapitel 2 Grundlagen
2.4.6 Spring
Das Rahmenwerk Spring verfolgt das Ziel, Java-Entwickler dabei zu unterstützen die Anwendung
gut und einheitlich zu strukturieren und dadurch die Weiterentwicklung zu vereinfachen. Dabei ist
Spring insbesondere eine leichtgewichtige Alternative zu den J2EE-Konzepten [ZLMG08, S. 19].
Es ist modular aufgebaut, so dass nicht alle Komponenten benutzt werden müssen und integriert
existierende Rahmenwerke, wie Hibernate.
Eine konkrete Funktionalität von Spring ist der Dependency Injection (DI) Mechanismus. Dieser
ermöglicht es, dass ein Objekt ein anderes Objekt eines bestimmten Typs erhalten kann, ohne dass
es dieses erzeugen und somit dessen konkrete Klasse kennen muss. In einer Konfigurationsdatei
wird z. B. bekannt gemacht, dass ein Objekt einer Klasse einen Service benötigt. Spring injiziert
diesen Service zur Laufzeit. Dieses folgt dem „Inversion of Control“-Prinzip.
Der DI-Mechanismus wird durch BeanFactorys geleistet. In Spring sind alle verwalteten Klas-
sen Beans, auch wenn sie es im Sinne von Abschnitt 2.4.1 nicht sind. Eine BeanFactory erzeugt
und verwaltet die Objekte und sorgt dafür, dass die korrekten Objekte injiziert werden (siehe
[ZLMG08, S. 35 ff.]).
2.5 Zusammenfassung
In diesem Kapitel wurden die für diese Arbeit nötigen Grundlagen eingeführt. Es wurde definiert,
dass Architektur die grundlegende Organisation eines Systems, bestehend aus Komponenten und
Beziehungen zueinander, darstellt. Architekturen lassen sich zu Architekturstilen zusammenfas-
sen. Architekturstile stellen zusammen mit einer Soll-Architektur den Vergleichspunkt dar, anhand
dessen eine Ist-Architektur bewertet wird und Architekturverletzungen gefunden werden können.
Architektur von Software ist wichtig, weil sie die Kommunikation unterstützt, Designentscheidun-
gen darstellt und auf hoher Ebene Wiederverwendung ermöglicht.
Performance ist der zweite zentrale Begriff dieser Arbeit und wird als messbare Leistungsfähig-
keit eines Softwaresystems definiert. Dabei wird zwischen dem Zeit- und dem Verbrauchsver-
halten unterschieden. Performance ist eine wichtige Qualitätseigenschaft, die über die Akzeptanz
einer Software entscheiden kann. Der Prozess der Verbesserung der Performance wird als Opti-
mierung bezeichnet. Grundlage dafür sind Messungen, die mittels Benchmarking und Profiling
durchgeführt werden. Dabei ist im Bezug auf Java besonders zu beachten, dass Java kein explizi-
tes Performance-Modell besitzt und die JVM mit Hilfe eines eingebauten Compilers optimiert.
Der Untersuchungsgegenstand dieser Arbeit ist das JCommSy, eine Web-Anwendung, die als Mi-
grationsprojekt aus dem CommSy entstanden ist. Das CommSy stellt eine Plattform dar, mit deren
Hilfe projektbasiertes Lernen unterstützt wird.
Servlet, JSP und Tag-Bibliotheken sind die Grundlage für in Java entwickelte Web-Anwendungen.
Sie erlauben eine Entwicklung auf hohem Abstraktionsniveau und ermöglichen eine Trennung von
Darstellung und Anwendungslogik.
25
Kapitel 3
Verwandte Themen
Performance und Architektur sind zwei wichtige Aspekte von Software (siehe 2.1.4 und 2.2.4). Es
gibt bereits viele Arbeiten, die die beiden Aspekte einzeln betrachten. In diesem Kapitel werden
Arbeiten vorgestellt, die sich mit dem Zusammenhang von Architektur und Performance beschäf-
tigen und deren Kernaussagen dargestellt.
3.1 Performance-Modelle und Performance Engineering
Soll die Performance einer Software untersucht werden, kann es hilfreich sein, von der Software
zu abstrahieren und damit bestimmte Aspekte auszublenden. Geschieht das mit der Absicht, Aus-
sagen über Performance zu machen, dann spricht man von der Erstellung eines Performance-
Modells. Mit dessen Hilfe können Aussagen über die Performance der Software gemacht, Vorher-
sagen abgeleitet oder Kriterien aufgestellt werden. Zur Notation eines Performance-Modells kann
z. B. das „UML Profile for Schedulability, Performance, and Time Specification“ (UML-SPT)
verwendet werden [uml05]. Das UML-SPT-Profil erweitert die bekannten Elemente der Unified
Modeling Language (UML) beispielsweise um Annotationen zur Angabe der benötigten Ausfüh-
rungszeiten.
Im Folgenden werden zunächst Arbeiten zu Performance-Modellen und anschließend darauf auf-
bauende Verfahren vorgestellt.
3.1.1 Architektur- und Performance-Modelle
Marco und Mirandola stellen einen Ansatz vor, um aus Architekturmodellen Performance-Modelle
zu erzeugen [MM06]. Die Autoren stellen fest, dass es besonders wichtig ist, nicht-funktionale
Anforderungen, wie die Performance, bereits auf der Architekturebene zu betrachten. Um die
Performance- und Architekturmodelle gemeinsam verwenden zu können, werden Transformatio-
nen benötigt, die die Modelle ineinander überführen. Verschiedene Performance-Modelle werden
vorgestellt und es wird herausgearbeitet, welche Eigenschaften die unterschiedlichen Transforma-
tionen besitzen. Eine Anforderung an die Transformationen ist die Automatisierbarkeit. Es wird
betrachtet, inwieweit die Transformation von einem Werkzeug automatisiert übernommen werden
kann.
Koziolek, Happe und Becker [KHB06] arbeiten in ihrem Artikel heraus, dass das frühzeitige Er-
kennen von Performance-Eigenschaften einer Architektur es ermöglicht, Designänderungen be-
reits zu Projektbeginn und damit günstiger durchzuführen. Insbesondere bei komponentenbasierter
Software (siehe [And03]), bei der Performance-Daten für die einzelnen Komponenten vorliegen,
26 Kapitel 3 Verwandte Themen
können auf dieser Grundlage schon frühzeitig Entscheidungen getroffen werden. Die Entwick-
ler einer Komponente kennen den genauen Einsatzkontext in der Regel nicht, deshalb müssen
die Performance-Daten mit dem Einsatzkontext (z. B. Hardwareausstattung, Art der übergebenen
Parameter) parametrisierbar sein. Die Autoren erweitern zu diesem Zweck das UML-SPT-Profil
um eine Möglichkeit, die Performance in Abhängigkeit der Parameter anzugeben. Sie stellen ein
Performance-Vorhersagemodell vor, das explizit die Einflüsse der Parameter und die Benutzung
externer Services berücksichtigt. Dabei arbeiten sie heraus, dass Eigenschaften der Komponenten-
parameter Einfluss haben und diese deshalb spezifizierbar sein müssen. Für eine übergebene Liste
von Elementen ist z. B. deren Länge ein Kriterium, von dem die Performance der Komponente
abhängen kann. Die Autoren zeigen auf, dass parameterabhängige Performance-Spezifikationen
zu besseren Vorhersagen führen. Ihr Modell lässt sich insbesondere bei Anwendungen mit großen
Datenflüssen anwenden.
Reussner und Hasselbring nehmen direkt Bezug auf den Zusammenhang von Architektur und
Performance. Sie stellen fest:
„Zusätzliche Schichten kosten tatsächlich ein paar Maschinenzyklen, aber das ist heu-
te kein Problem“ [RH06, S. 100]
Dies wird damit begründet, dass Performance an anderer Stelle beachtet werden soll, z. B. beim
Datenbankzugriff. Dieser muss so gekapselt sein, dass er später gezielt optimiert werden kann.
Die Architektur sollte flexibel genug sein, dass der Zeitpunkt des Datenbankzugriffs geregelt und
angepasst werden kann.
3.1.2 Software architecture-based performance engineering
„Software performance engineering“ beschreibt die Disziplin, Software so zu entwickeln, dass
sie den Performance-Ansprüchen genügt1. Dabei wird zwischen dem traditionellen und einem
auf Software-Architektur basierenden Ansatz unterschieden. Traditionell wird zuerst die Software
entwickelt und Performance-Fragen ignoriert. Erst während der Integration und der zugehörigen
Testphase wird die Performance betrachtet. Wenn die Performance sich zu diesem Zeitpunkt als
ausreichend herausstellt, dann ist das Vorgehen gerechtfertigt und es gab keine Zusatzkosten.
Wenn die nicht ausreichende Performance, Änderungen nötig macht, dann können diese weit-
reichend und damit teuer sein. Werden die Performance-Probleme erst spät gefunden, kommt es
außerdem zum Problem, den Zeitplan einzuhalten. Ein weiterer Nachteil ist, dass die anderen
nicht-funktionalen Anforderungen eventuell aufgeweicht werden, wenn die Quelltextoptimierun-
gen durchgeführt werden. Beispielsweise kann die Wiederverwendbarkeit unter der Optimierung
leiden (siehe 2.2.5).
Im Gegensatz zu dem traditionellen Ansatz, werden bei dem auf Software-Architektur basieren-
den Ansatz von Anfang an Performance-Aspekte mit behandelt. Bereits bei den ersten Entwurfs-
entscheidungen wird stets die Frage gestellt, ob sie den Performance-Anforderungen genügen.
1Gleichzeitig heißt eine konkrete Methodik auch Software Performance Engineering (SPE).
3.2 Generische Leistungsbewertung einer Mehrschichtenarchitektur 27
Dafür gibt es Verfahren und Werkzeuge, die den Architekten dabei unterstützen. Der Vorteil die-
ses Vorgehens ist, dass Performance-Probleme bereits früh aufgedeckt werden und entsprechende
Änderungen zu diesem Zeitpunkt noch nicht so aufwendig und teuer sind. Der Nachteil ist, dass
die Entwicklung langsamer ist, da ein weiterer Aspekt behandelt wird. Der Performance-Aspekt
wird auch dann behandelt, wenn er gar kein Problem ist und im traditionellen Ansatz keine Kosten
verursachen würde [Kau03].
Balsamo, Marco, et al. [BMIS04] arbeiten heraus, dass Performance-Analysen noch nicht in den
typischen Softwareentwicklungsprozess integriert sind. Sie stellen Performance-Analyseverfahren
vor und bewerten, wie sie sich in den Entwicklungsprozess integrieren lassen. Ihre Arbeit gibt eine
Übersicht über die vorhandenen Tools und deren Schwächen und Stärken.
Eines der Performance-Analyseverfahren ist Software Performance Engineering (SPE, siehe auch
[RH06, S. 321] und [CKK02, S. 257]). SPE war der erste Ansatz, der Performance-Analyse in
den Entwicklungsprozess integriert. Darauf aufbauend stellen Williams und Smith PASA vor
[WS02]. PASA steht für Performance Assessment of Software Architectures. Dieses szenario-
basierte Vorgehen ermöglicht das frühzeitige Finden von Risiken, wie nicht erfüllte Performance-
Anforderungen. Darüber hinaus gibt es dem Anwender Strategien zur Hand, wie damit umgegan-
gen werden kann.
3.2 Generische Leistungsbewertung einer Mehrschichtenarchitektur
Kune [Kun06] untersucht generische Leistungsbewertung einer Server-basierten Mehrschichten-
architektur auf Basis von der Java Enterprise Edition (J2EE). Der Begriff Leistungsbewertung wird
bei Kune ähnlich, aber nicht gleichbedeutend wie Performance-Analyse, in dieser Arbeit verwen-
det. Der Unterschied liegt darin, das Kune explizit keine Veränderungen am untersuchten System
durchführt. Darüber hinausgehend, wird bei der Untersuchung ein Zugriff auf den Quelltext der
Anwendung explizit ausgeschlossen und dementsprechend generische Konzepte erarbeitet.
Für den generischen Ansatz müssen die betrachteten Systeme eine strikte Implementierung der
J2EE-Architektur sein, also insbesondere aus einem Web-Modul und einem Logik-Modul beste-
hen. Das Web-Modul läuft in Form eines „Web Application Archive“ (WAR) in einem Servlet
Container und enthält keine Fachlogik. Diese läuft in Form von Enterprise JavaBeans (EJB) in
einem EJB-Container. Die Datenbankschicht liegt wiederum darunter und wird per Java Database
Connectivity (JDBC) angesprochen. Alle drei Schichten können zur Laufzeit beobachtet oder sta-
tisch analysiert werden.
Für die Leistungsanalyse werden die drei „Prüfkomponenten“ Filter, Stubs und Driver verwendet.
Die Filter werden einer Schicht vorgelagert und reichern diese um Aufzeichnungsfunktionalitäten
an. Ein Stub wird verwendet, um eine untere Schicht zu simulieren, deren Vermessung zu umge-
hen und Seiteneffekte auszuschließen. Die Driver-Komponenten erzeugen die Anfragen, die die
zu vermessenden Leistungen auslösen. In der vorliegenden Arbeit wird JMeter verwendet um An-
fragen zu erzeugen (siehe 5.3.1). JMeter nimmt die Rolle eines Drivers nach Kunes Terminologie
28 Kapitel 3 Verwandte Themen
ein. Die drei Prüfkomponenten erlauben es, Performance-Daten nicht nur für die ganze Anwen-
dung, sondern auch für die einzelnen Schichten zu erfassen.
Kune stellt fest, dass es möglich ist, J2EE-basierte Anwendungen auf ihre Leistungsfähigkeit hin
zu untersuchen, ohne den Quelltext zur Verfügung zu haben. Dafür wird eine Striktheit bei der
Trennung der Schichten gefordert, die im JCommSy nicht gegeben ist. Für Kunes Ansatz müssen
die Schichten in Form von einzelnen J2EE-Modulen vorliegen. Der Ansatz bietet keine Mög-
lichkeit, die Performance eines dieser Module genauer zu untersuchen, um Aussagen zu finden,
welche Packages oder Klassen einer Schicht für die Performance der Schicht ausschlaggebend
sind.
3.3 Performance-Antipattern
Antipattern stellen, wie Entwurfs- oder Architekturmuster, Lösungsmöglichkeiten für wiederkeh-
rende Probleme dar. Im Gegensatz zu Entwurfsmustern (siehe [GHJV95]) haben die in den Anti-
pattern beschriebenen Lösungen negative Auswirkungen und sollten nicht umgesetzt werden. Zu
einem Antipattern gehört immer eine alternative Lösung, die beschreibt, wie das Problem besser
gelöst werden kann.
Performance Antipattern sind speziell auf Performance zugeschnittene Antipatterns. Bezüglich
Performance bieten sich Antipattern besonders an, da gute Performance sich durch die Abwesen-
heit von schlechten Lösungen auszeichnet. Smith und Williams haben einen Katalog von Perfor-
mance Antipattern angefangen (siehe [SW00] und [SW02]). Die dort aufgeführten Antipattern
bestehen aus einem Namen, dem Problemfall, in dem sie mit ihren negativen Auswirkungen an-
gewendet werden, und einer Lösung, die beschreibt, wie sie zu vermeiden sind.
Ein Beispiel für ein Antipattern ist „God Class“. Eine God Class entsteht, wenn eine Klasse viel
Funktionalität oder viele Daten enthält. Das Prinzip, Daten und Verhalten zusammen zu halten,
ist verletzt. Übermäßige Kommunikation mit der God Class ist die Folge. Dies ist insbesondere
dann Performance kritisch, wenn Nebenläufigkeit oder entfernte Methodenaufrufe dazu kommen.
Vermieden wird eine God Class, indem die Daten zusammen mit dem zugehörigen Verhalten auf
einzelne Klassen verteilt werden. Eine ausführliche Beschreibung des God Class Antipatterns fin-
det man bei Smith und Williams [SW00].
3.4 Zusammenfassung
In diesem Kapitel wurden verwandte Arbeiten vorgestellt und in Bezug zu dieser Arbeit gestellt.
Mit UML-SPT gibt es eine Sprache, um Performance-Modelle zu erstellen. Software Performance
Engineering ist ein Verfahren, dass die Betrachtung von Performance-Kriterien in den Entwick-
lungsprozess integriert. Performance Antipattern können Entwickler für Performance-Aspekte
sensibilisieren und einen Katalog von zu vermeidenden Lösungsmöglichkeiten bilden.
29
Kapitel 4
Architekturanalyse
In diesem Kapitel wird die Architektur des JCommSy analysiert. Das Ziel und das Vorgehen der
Analyse wird erklärt und die dafür nötigen Software-Werkzeuge werden vorgestellt. Abschließend
werden die Ergebnisse am Beispiel des JCommSy dargestellt.
4.1 Ziel
Während einer Architekturanalyse wird untersucht, inwieweit die Ist-Architektur einer Software
mit ihrer Soll-Architektur übereinstimmt. Resultate der Analyse sind Änderungen am Software-
system (also an der Ist-Architektur) und/oder an der Soll-Architektur. Eine Architekturanalyse
kann während des gesamten Lebenszykluses eines Projekts stattfinden, sobald ein erster Prototyp
vorhanden ist. Im Idealfall wird während der Entwicklungszeit regelmäßig die Architektur ana-
lysiert, um ein Auseinanderlaufen von Ist- und Soll-Architektur zu verhindern. Die Gründe für
dieses Auseinanderlaufen sind vielfältig. Die fachlichen Anforderungen ändern sich im Laufe der
Entwicklung und nicht immer wird eine nötige Änderung an der Architektur durchgeführt, bzw.
die Auswirkungen auf die Architektur berücksichtigt. Entwickler weichen, gerade unter Zeitdruck
oder wenn die Soll-Architektur nicht ausreichend kommuniziert wird, von den Vorgaben der Soll-
Architektur ab. Details zum Auseinanderlaufen von Soll- und Ist-Architektur findet man in der
Arbeit von Lilienthal [Lil08, S. 41 f.].
4.1.1 Abgrenzung: Architekturbewertung
Der Begriff Architekturanalyse wird teilweise synonym mit dem Begriff Architekturbewertung
verwendet (z. B. von Clements, Kazman und Klein [CKK02] oder Bass, Clements und Kazman
[BCK98]). In diesem Unterabschnitt werden beide Begriffe voneinander abgegrenzt und die Ver-
wendung in dieser Arbeit geklärt.
Im Gegensatz zur eingeführten Architekturanalyse ist das Ziel einer Architekturbewertung eine
(meist frühe) Machbarkeitssicherstellung, indem überprüft wird, ob die Architektur den Anforde-
rungsspezifikationen entspricht. Reussner und Hasselbring definieren Architekturbewertung wie
folgt:
Definition 4.1 (Architekturbewertung) „Die Architekturbewertung umfasst alle Aktivitäten zur
qualitativen oder quantitativen Bestimmung der Qualitätseigenschaften einer Architekturspezifi-
kation. Somit dient die Architekturbewertung der Überprüfung der Qualität der Architekturspezi-
fikation.“ [RH06, S. 282]
30 Kapitel 4 Architekturanalyse
Im Folgenden wird dementsprechend der Begriff Architekturbewertung verwendet, wenn es dar-
um geht, ob ein System, welches die Architektur umsetzt, die gewünschten Eigenschaften erfüllen
können wird.
Das Bewerten kann mit Hilfe unterschiedlicher Methoden erfolgen. Es werden zwei Kategori-
en unterschieden: qualitative und quantitative Techniken (siehe [RH06, S. 284]). Um z. B. die
Softwarequalität Performance anhand einer Spezifikation zu bewerten, bietet sich die quantitative
Technik Simulation an. Auf diese Weise können, im Gegensatz zur Architekturanalyse, bereits
Aussagen gemacht werden, bevor ein Prototyp existiert. Für weiterführende Informationen über
Architekturbewertungsverfahren, wie Architecture Tradeoff Analysis Method (ATAM), Software
Architecture Analysis Method (SAAM) oder Active Reviews for Intermediate Designs (ARID),
siehe Clements, Kazman und Klein [CKK02]. Detailliertere Informationen bezüglich der Bewer-
tung der Performance erhält man bei Reussner und Hasselbring [RH06, S. 311 ff.].
4.2 Software-Werkzeuge
Bevor im nächsten Abschnitt das Vorgehen bei der Architekturanalyse vorgestellt wird, wird in
diesem Abschnitt ein Überblick über dafür geeignete Software-Werkzeuge gegeben. Dabei werden
insbesondere die während dieser Diplomarbeit eingesetzten Software-Werkzeuge Sotograph und
Sotoarc vorgestellt.
4.2.1 Sotograph
Die Software Sotograph1 erzeugt zu einer bestehenden Software ein Modell, welches in einer
relationalen Datenbank abgelegt wird. Es enthält z. B. Informationen darüber, welches Artefakt
welche anderen Artefakte benutzt. Mit Hilfe von SQL-artigen Anfragen können Eigenschaften
abgefragt und abgeleitet werden. Der Sotograph definiert bereits eine ganze Reihe von Maßen2.
Es lässt sich z. B. abfragen, welche Klassen durch besonders viele oder wenige Methoden auf-
fallen. Eine besonders große Anzahl von Methoden kann ein Anzeichen für eine God Class sein
(siehe 3.3). Hat eine Klasse wenig Methoden, kann dies darauf hindeuten, dass die Methoden in
andere Klassen gehören und die Klasse keine Daseinsberechtigung hat. Neben diesem Maß, gibt
es noch weitere vorgefertigte Maße und die Möglichkeit eigene zu erstellen. Die Maße sind gu-
te Indikatoren für Schwachstellen in der Software-Architektur und können zur Verbesserung der
Qualität der Software beitragen [SM02].
Der Sotograph ermöglicht es, diese Informationen auf höhere Ebenen zu aggregieren. Beispiels-
weise kann aus dem Maß, welche Klassen sich in einem Zyklus gegenseitig benutzen, eine Zy-
klenansicht auf Package-Ebene erzeugt werden.
Des Weiteren kann im Sotograph ein Architekturmodell spezifiziert werden und darüber z. B. fest-
gelegt werden, welche Packages und Klassen welchen Subsystemen angehören. Es kann festge-
1http://www.hello2morrow.com/products/sotograph2In der Literatur wird anstelle des Begriffs Maß auch Metrik verwendet.
4.2 Software-Werkzeuge 31
Abbildung 4.1: JCommSy im Sotoarc (Benutzungsoberfläche)
legt werden, welche Artefakte oder Subsysteme ein Subsystem benutzen darf. Verletzungen dieser
Vorgaben stellt der Sotograph entsprechend dar.
4.2.2 Sotoarc
Die Software Sotoarc3 baut auf dem in Abschnitt 4.2.1 beschriebenen relationalen Datenbankmo-
dell der Sotograph Software auf. Der Schwerpunkt liegt jedoch nicht darauf, Maße zu ermitteln.
Sotoarc ermöglicht es, die Architektur zu visualisieren, graphisch zu modellieren und zu überprü-
fen.
Abbildung 4.1 zeigt die Benutzungsoberfläche des Sotoarc, in der die Struktur des JCommSy dar-
gestellt wird. Die Linien zwischen den Elementen zeigen Abhängigkeiten auf, wobei die grünen
erlaubt sind und die roten nicht. Auf der linken Seite sind sie von oben nach unten zu lesen, rechts
von unten nach oben. Beispielsweise ist migration abhängig von servlet, aber nicht von tags.
Desweiteren sieht man, dass innerhalb der Service-Schicht api und impl jeweils voneinander ab-
hängig sind, was an dieser Stelle aber erlaubt ist.
3http://www.hello2morrow.com/products/sotoarc
32 Kapitel 4 Architekturanalyse
Wie schon beim Sotograph werden die Informationen dabei aggregiert. Sie lassen sich bei Bedarf
so weit verfeinern, bis man genau die Quelltextzeile erfährt, die die Abhängigkeit auslöst. Auf die-
se Weise erhält man eine Visualisierung der Beziehungen, sowohl auf abstrakter Ebene, als auch
bei Bedarf detailliert.
Die Möglichkeiten gehen über das reine Betrachten hinaus. Der Sotoarc bietet die Möglichkeit
Subsysteme zu definieren. Innerhalb solcher Subsysteme können wiederum Subsysteme definiert
werden. Packages lassen sich den Subsystemen zuordnen. Mit Hilfe der Subsysteme können die
Architekturvorstellungen abgebildet werden. Subsysteme können z. B. Schichten sein. Ein Sub-
system welches weiter unten liegt, darf dann nicht auf darüberliegende zugreifen. Ordnet man
Packages den Subsystemen zu, dann werden Beziehungen, die die Schichtung verletzen, durch
rote Linien markiert. In Abbildung 4.1 sieht man, dass die Logik-Schicht unerlaubte Zugriffe
auf Klassen aus den Ausstattern und Fragmenten hat. Desweiteren ist das impl-Package aus der
Service-Schicht als private markiert und damit der Zugriff aus dem Migrationspackage verboten.
Damit bietet der Sotoarc die Möglichkeit die Architektur zu analysieren, also insbesondere aufzu-
zeigen, wo die Ist- von der Soll-Architektur abweicht.
Packages können aus einem Subsystem in ein anderes verschoben werden und es gibt unmittelbar
Feedback darüber, wie sich die Benutzt-Beziehungen verändert haben. Beispielsweise wird sofort
dargestellt, ob durch die Verschiebung neue Verletzungen entstehen. Es lassen sich neue Subsyste-
me erstellen und/oder die Eigenschaften über erlaubte Abhängigkeiten verändern. Es ist möglich,
festzulegen, dass zwei Subsysteme eines anderen Subsystems sich gegenseitig nicht benutzen dür-
fen. Insgesamt bietet der Sotoarc damit die Möglichkeit die Architektur zu modellieren.
Der Sotoarc unterstützt den Architekten dabei, die Software anhand der durchgeführten Verschie-
bungen umzustrukturieren. Dies ist nötig, da sich die gemachten Änderungen nur auf das relatio-
nale Modell der Software in der Datenbank auswirken, nicht jedoch auf den eigentlichen Quelltext.
Dafür gibt es einen Refactoring-Report. Dieser enthält jede Veränderung, die am Softwaresystem
durchgeführt werden muss, um die Software in die neue Struktur zu überführen.
4.2.3 Alternativen
Es gibt weitere Werkzeuge, um die Architektur einer Software zu analysieren. Dazu zählen un-
ter anderem JDepend, XRadar, Lattix und SonarJ (siehe [Lil08, S. 64]). Eine Gegenüberstellung
einiger Werkzeuge gibt es von Scharping [Sch05, S. 14 f.]. Im Rahmen dieser Arbeit wird der So-
tograph und insbesondere Sotoarc verwendet und auf die Alternativen nicht weiter eingegangen.
4.3 Vorgehen
Nachdem im letzten Abschnitt die Architekturanalyse-Werkzeuge vorgestellt wurden, wird jetzt
das Vorgehen und deren Benutzung am JCommSy beschrieben. Dabei wird zunächst generell und
anschließend konkret für das JCommSy beschrieben, welche Schritte durchgeführt wurden. Die
Ergebnisse werden im nächsten Abschnitt dargestellt.
4.3 Vorgehen 33
4.3.1 Allgemeines zum Vorgehen
Lilienthal arbeitet in ihrer Dissertation heraus, dass Architekturanalysen mit Hilfe von Software-
Werkzeugen und Interviews mit den beteiligten Architekten durchgeführt wird [Lil08, S. 61]. Die
hier beschriebene Analyse wurde zusammen mit zwei weiteren Diplomanden durchgeführt (siehe
[Dar09] und [Thi09]). Da alle drei Beteiligten bereits Kenntnisse über die zu betrachtende Soft-
ware haben und selbst an der Entwicklung beteiligt sind und waren, wurde auf die Interviews
weitgehend verzichtet. Die Einarbeitung in die Architekturvorstellungen des Enwicklungsteams
konnte somit entfallen. Aus demselben Grund konnte der Wechsel zwischen Analyse und Umset-
zungen der Umstrukturierungen häufig geschehen und die einzelnen Phasen kurz gehalten werden.
Abgesichert wurden die Überlegungen und Erkenntnisse durch Diskussion und Rücksprache mit
einem der Hauptverantwortlichen und Initiatoren der zu analysierenden Software.
4.3.2 Vorgehen beim JCommSy
Die Architektur des JCommSy war bereits Untersuchungsgegenstand mehrerer Diplom- und Stu-
dienarbeiten (siehe [Sch08b], [Sch05]). Die Arbeit von Schwentner enthielt die aktuellste Archi-
tekturbeschreibung des JCommSy und war Grundlage dieser Analyse. Sie lag jedoch nicht in einer
für den Sotoarc oder Sotograph geeigneten Form vor. Zunächst wurde deshalb die aktuelle Versi-
on des JCommSy im Sotograph eingelesen. Mit dem Sotoarc wurde eine Architekturbeschreibung
modelliert. Diese verfeinerte die Beschreibung von Schwentner und reicherte sie um weitere In-
formationen an. Es wurde vom Großen zum Kleinen vorgegangen, d. h. zuerst wurde nur eine
Einteilung in Schichten vorgenommen, später wurden die einzelnen Schichten genauer model-
liert.
Es stellten sich zwei Aspekte als besonders schwierig heraus. Zum einen die Frage, aus wie vie-
len Schichten das JCommSy besteht und wie diese angeordnet sind. Einhergehend damit wurde
erörtert, ob eine Schichtung genug Informationen transportieren kann oder ob ein anderer Archi-
tekturstil, wie z. B. eine Subsystemarchitektur als Grundlage gewählt werden soll. Zum anderen
wurden die Namensgebungen kontrovers diskutiert, insbesondere als die Schichtung weiter ver-
feinert wurde.
Nachdem sich eine erste Version der Schichtung etabliert hatte, wurde diese im Sotoarc als Modell
hinterlegt. Die einzelnen Packages des JCommSy wurden manuell den Schichten zugeordnet. Der
Sotoarc zeigte eine Reihe von Architekturverletzungen an, die einzeln untersucht wurden. Mit
dem Sotoarc wurde dann evaluiert, ob sich eine Verletzung beseitigen ließe, indem die entspre-
chende Klasse in eine andere Schicht verschoben würde. Wenn dies der Fall war und die Klasse
auch inhaltlich sinnvoll in der anderen Schicht angeordnet war, wurden die nötigen Umbaumaß-
nahmen erfasst. Diese Refactorings wurden umgesetzt und der Quelltext entsprechend angepasst.
Zu diesen Refactorings gehörte auch die Umbenennung von Klassen und die Umgestaltung der
Package-Struktur. Dadurch spiegelt die Package-Struktur die Schichtung fast direkt wider. Ein
Modell für den Sotoarc kann dementsprechend einfach aus der Package-Struktur extrahiert wer-
den, wobei dafür weiterhin manuelle Eingriffe erforderlich sind.
34 Kapitel 4 Architekturanalyse
Es wurde zwischen der Analyse mittels Sotoarc und dem Umsetzen der ermittelten Refactorings
hin und her gewechselt, bis die Schichtung keine Verletzung mehr enthielt. Für die einzelnen
Schichten wurde die Architektur weiter verfeinert. Innerhalb der einzelnen Schichten wurden Re-
geln definiert welche Subschichten bzw. Subpackages es gibt und welche Regeln für diese gelten.
Um diese Regeln für die Entwickler präsent zu halten, wurde versucht sie z. B. über Namenskon-
ventionen in die Package-Struktur einfließen zu lassen (siehe Service-Schicht in 4.4.1).
4.4 Architektur des JCommSy
Im Rahmen dieser und der Diplomarbeiten von Daricili [Dar09] und Thiesen [Thi09] wurde die
Architektur mit dem Sotoarc analysiert und die Soll-Architektur weiterentwickelt. Um die Ist-
Architektur mit der Soll-Architektur in Einklang zu bringen, wurden die dafür nötigen Umstruktu-
rierungen am System umgesetzt. Die resultierende Architektur wird in den folgenden Abschnitten
vorgestellt.
In den Jahren 2005 und 2006 wurde die Architektur des JCommSy von Lilienthal analysiert [Lil08,
S. 177 ff.].
4.4.1 Architekturmodell
Die Architektur des JCommSy lässt sich, wie in Abbildung 4.2 dargestellt, als Schichtenarchitek-
tur auffassen. Ganz oben liegt die Präsentationsschicht. Diese enthält neben den technologieabhän-
gigen Darstellungskomponenten Servlet, tag und JSP (siehe 2.4.2, 2.4.4 und 2.4.3) die JCommSy
spezifischen Darstellungskomponenten Ausstatter und Fragmente. Die dargestellte Logik-Schicht
ist noch nicht im JCommSy enthalten, soll jedoch später die fachliche Logik enthalten. Diese ist
derzeit noch in den Ausstattern enthalten. Services stellen die fachlichen Objekte aus der Item-
Schicht zur Verfügung und verwalten deren Persistierung in der Datenbank. Dafür wird der OR-
Mapper Hibernate (siehe 2.4.5) eingesetzt. Die unterste Schicht enthält CommSy spezifische Wert-
artige Objekte wie z. B. ItemType oder ItemID.
Das Util-Subsystem wird aus allen Schichten außer Domainvalue benutzt, benutzt selber aber kei-
ne der Schichten und ist deshalb an der Seite angeordnet. Die Schichtung ist nicht strikt, d. h. es
wird nicht nur auf die direkt unter einer Schicht liegende Schicht zugegriffen. Die Beziehungen
sind nicht explizit durch Pfeile dargestellt, da alle erlaubten Benutzt-Beziehungen tatsächlich vor-
handen sind. Die einzige Ausnahme ist, dass die Domainvalue-Schicht nicht auf die Util-Schicht
zugreift.
Die Subsysteme des JCommSy geben die Package-Struktur des JCommSy vor. Sie werden im
Folgenden detaillierter vorgestellt.
• util enthält Hilfsklassen, die sich nicht in eine Schichtung eingliedern lassen, z. B. weil
sie von vielen oder allen Schichten aus benutzt werden. Klassen, die in diesem Package
liegen, dürfen keine Klassen aus anderen JCommSy-Packages benutzen. Ein Beispiel ist die
CommsyException.
4.4 Architektur des JCommSy 35
domainvalue
item
service
logic
presentation
util
Schichtung des JCommSy Präsentationsschicht (detailliert)
servlet jsp, tag Technologie
outfitter fragment Struktur
Service-Schicht (detailliert)
api Schnittstelle
impl Implementierung
Abbildung 4.2: Architektur des JCommSy
• domainvalue enthält Wert-artige JCommSy spezifische Typen. Es ist eine passive Schicht,
die von den anderen benutzt wird, ohne selber andere zu benutzen. Beispiele für Fachwerte
sind ItemID und FilterPattern.
• item enthält die fachlichen Klassen des JCommSy, welche persistiert werden. Das Package
enthält Klassen wie Group oder User, die die entsprechenden Gegenstände der CommSy-
Welt darstellen.
• services bieten die Möglichkeit, Items zu erhalten, zu speichern, zu erstellen und zu lö-
schen. Sie haben damit eine CRUD-artige Schnittstelle. CRUD stammt aus der Datenbank-
welt und kürzt die grundlegenden Datenbankoperationen „create“, „retrieve“, „update“ und
„delete“ ab. Darüber hinaus sichern sie fachliche Konsistenz zu, sorgen also z. B. dafür, dass
beim Speichern die ItemIDs korrekt gesetzt sind und Informationen darüber, wer das Item
zuletzt verändert hat, gespeichert werden.
Die Services sind von außerhalb über das Package services.api zu benutzen. Auf diese
Weise ist die Verwendung des Hibernate-Rahmenwerks vollständig in der Service-Schicht
gekapselt. Ein Beispiel für ein Element dieser Schicht ist UserService.
• logic soll die fachliche Logik enthalten, die das JCommSy ausmacht. Dieses Package ist
noch leer, entsprechende Funktionalität ist somit fälschlicherweise in anderen Subsystemen.
Im Laufe dieser Arbeit wird die Fachlogik in diese Schicht extrahiert werden.
• presentation.fragments enthält Fragmente. Diese Bean-artigen Klassen (siehe 2.4.1) sind
Datenbehälter für die Präsentationsschicht. Sie werden von den Ausstattern mit den darzu-
stellenden Inhalten befüllt. Die JSPs benutzen sie, um die anzuzeigenden Daten zu erhalten.
Wie auch die JSPs, werden die Fragmente in einer Baumstruktur zusammengestellt. Ein
Fragment entspricht einer JSP und enthält die dafür nötigen Daten. Ein Beispiel ist die
36 Kapitel 4 Architekturanalyse
Klasse AnnouncementDetail, welche Informationen über eine Ankündigung enthält, wie sie
in der Detailansicht dargestellt werden.
• presentation.outfitter enthält die Ausstatter. Diese sind ebenfalls in einer Baumstruktur
zusammengesteckt und befüllen die zugehörigen Fragmente. Durch die Einführung einer
JavaBean, der ParamBean, ist dieses Subsystem bereits von dem Servlet-Rahmenwerk ent-
koppelt (siehe [Sch08b]). Der GroupDetailOutfitter liest beispielsweise aus dem entspre-
chenden Group-Item die Werte und schreibt diese in das GroupDetail-Fragment.
• presentation.tags und JSP enthalten JSPs, welche das Layout und die HTML Struktur
enthalten. Sie sind direkt für die Darstellung zuständig und über die Fragmente von den
Items entkoppelt. Die JSPs sollen keine Logik enthalten. Entsprechende Anzeigelogik wird
deshalb in tag-Klassen ausgelagert (siehe 2.4.4). JSPs und tags sind die einzigen Elemente
des JCommSy in denen die Anzeigesprache HTML verwendet wird.
• presentation.servlet stellt mit den Servlets den zentralen Einstiegspunkt in das System
zur Verfügung. Das CommsyServlet nimmt die ankommende HTTP-Anfrage entgegen, wer-
tet diese aus und stößt den Aufbau des entsprechenden Ausstatter-Baums an. Das Servlet
selber greift auf Services und Items zu, z. B. um zu überprüfen, ob ein Benutzer angemeldet
ist.
• migration ist ein zusätzliches Package, dass eine Sonderrolle hat und deswegen nicht in der
Architektur auftaucht. Es enthält die Klassen, die dafür zuständig sind, dass das JCommSy
zusammen mit dem PCommSy betrieben werden kann. Dieses Package verschwindet, so-
bald das JCommSy alle Funktionalität des PCommSy enthält und darf dementsprechend von
keinem anderen Subsystem benutzt werden. Es enthält z. B. den MigrationFilter, einen
Servlet-Filter, der eine Umleitung ins PCommSy veranlasst, wenn eine Funktionalität ange-
fordert wird, die noch nicht vom JCommSy erbracht werden kann.
Im Rahmen der Umstrukturierung wurde die bereits teilweise umgesetzte Idee, die Service-Schicht
in Schnittstelle und Implementierung zu unterteilen, komplett umgesetzt. Dies vereinfacht die Be-
nutzung und kapselt die Hibernate-spezifischen Anteile.
Die zweite große Neuerung betrifft die Strukturierung des presentation-Packages. Die Auftei-
lung in die vier Subsysteme und die Anreicherung mit Informationen über erlaubte Beziehungen
hat folgende Vorteile. Es gibt einen technologieabhängigen (Servlet, JSP, Tags) und einen techno-
logieunabhängigen (Ausstatter, Fragmente) Teil. Da weder Ausstatter noch Fragmente von dem
technologieabhängigen Teil abhängen, können diese wiederverwendet werden, wenn die Techno-
logie ausgewechselt wird. Die Unterteilung vereinfacht die Weiterentwicklung – ein Entwickler
muss sich nicht mehr mit dem Servlet-Rahmenwerk auseinandersetzen, um die Ausstatter weiter
zu entwickeln. Die Ausstatter können aufgrund ihrer Unabhängigkeit vom Servlet-Rahmenwerk
einfacher getestet werden [Sch08b, S. 31].
Die Struktur und die Namen der Ausstatter- und Fragment-Klassen haben sich seit ihrer Einfüh-
4.5 Zusammenfassung 37
rung (siehe [Hoh07]) gewandelt. Im Rahmen dieser Arbeit wurden die Package-Struktur bereinigt
und zusammen mit den Namen an die aktuelle Sicht auf das System angepasst.
4.4.2 Verletzungen
Die Ist-Architektur deckt sich weitgehend mit der Soll-Architektur. Eine Verletzung ist das leere
logic-Package. In diesem Package sollte Fachlogik enthalten sein. Dies beinhaltet z. B. Funktio-
nalität zum Überprüfen, ob die eingegebenen Werte eine gültige Ankündigung ergeben und deren
Speicherung in einem fachlichen Item. Diese Funktionalität ist über das System verteilt. Sie ist zu
großen Teilen in den Ausstattern und zum Teil implizit in den Servlets enthalten (siehe auch Abbil-
dung 6.1). Dies ist unerwünscht, da die Fachlogik unabhängig von der Anzeigelogik sein soll. Eine
Verzahnung beider macht es schwieriger, die Fachlogik mit einer alternativen Oberflächentechno-
logie wiederzuverwenden. Zusätzlich verkompliziert es die Entwicklung, da der Entwickler sich
mit zwei unabhängigen Aspekten gleichzeitig beschäftigen muss. Im Rahmen dieser Arbeit wer-
den Refactorings durchgeführt, deren Ziel es ist, die Fachlogik aus den Ausstattern zu extrahieren
und in eigenen Klassen im logic-Package verfügbar zu machen (siehe 6.1.2).
4.5 Zusammenfassung
Ziel der durchgeführten Architekturanalyse war es, die Ist- mit der Soll-Architektur zu verglei-
chen und dabei Schwachstellen, sowie daraus resultierende Änderungsvorschläge, zu identifizie-
ren. Durchgeführt wurde die Analyse mit dem Sotoarc. Der Sotoarc ist ein Programm, dass ein
Softwaresystem einliest, in einem Modell ablegt und dem Entwickler einen Blick auf das Sys-
tem auf einem höheren Abstraktionsniveau ermöglicht. Dafür wurden Subsysteme definiert, Re-
geln eingeführt wie die unterschiedlichen Subsysteme miteinander zusammenhängen und diese
dann am JCommSy validiert. Nötige Änderungen am JCommSy wurden identifiziert und umge-
setzt. Die resultierende Schichtenarchitektur des JCommSy, enthält insbesondere eine detaillierte
Unterteilung der Präsentationsschicht sowie eine explizite Schnittstelle der Service-Schicht. Als
ausstehendes Refactoring wurde das Extrahieren der Fachlogik in die Logik-Schicht identifiziert.
38 Kapitel 4 Architekturanalyse
39
Kapitel 5
Performance-Analyse
Wie in Abschnitt 2.2 ausgeführt, ist Performance ein Qualitätsmerkmal einer Software. Ziel die-
ses Kapitels ist es, einen Überblick über Performance-Aspekte des JCommSy zu erlangen und
Optimierungsmöglichkeiten dieses Qualitätsmerkmals zu identifizieren. Dafür wird der Vorgang
Performance-Analyse zunächst eingeführt. Es wird beschrieben mit welchem Ziel sie durchge-
führt wird, wie vorgegangen wird und welche Werkzeuge dabei eingesetzt werden. Abschließend
werden die Ergebnisse dargestellt und bewertet.
5.1 Ziel
Der Begriff Performance-Analyse beschreibt den Prozess, die Performance eines Systems zu un-
tersuchen, Änderungen zu identifizieren, umzusetzen und deren Auswirkungen zu erfassen. Das
Ziel dabei ist, die Performance (siehe Abschnitt 2.2) des Systems zu verbessern. Der Performance
stehen insbesondere die Architektur und die Lesbarkeit des Quelltextes gegenüber. Es ist nicht
das Ziel, die maximale Performance zu erreichen und dabei die Lesbarkeit des Quelltextes zu ver-
nachlässigen. Genausowenig sollen unüberlegt Architekturregeln verletzt oder verworfen werden,
nur um die Performance zu verbessern. Ziel ist es, eine Lösung zu finden, in der diese Aspekte
Beachtung finden. Dafür werden die Änderungen unter den verschiedenen Aspekten beleuchtet.
5.2 Vorgehen
Um die Performance-Analyse durchführen zu können, werden Software-Werkzeuge und das Sys-
tem, dass untersucht werden soll, benötigt. In diesem Abschnitt wird das System vorgestellt und
die nötigen Software-Werkzeuge eingeführt, wobei auf die besonderen Umstände einer Web-
Anwendung eingegangen wird.
5.2.1 Testumgebung
Um die Einflüsse durch äußere Faktoren so klein wie möglich zu halten, werden alle Performance-
Messungen auf einem eigens dafür eingerichteten Testsystem durchgeführt. Dieses Testsystem
wurde während dieser Diplomarbeit weder bezüglich der Hardware noch bezüglich der Software
verändert. Das JCommSy wird auf einem anderen System entwickelt und kompiliert. Das resul-
tierende Web Application Archive (kurz WAR) wird in den Tomcat des Testsystems geladen1. Die
WAR-Dateien werden archiviert, um alte Systemzustände erneut testen zu können.
1Das korrekte englische Wort ist „to deploy“.
40 Kapitel 5 Performance-Analyse
Im Mittelpunkt dieser Arbeit stehen nicht die Aussagen über die absolute Leistung, sondern z. B.
darüber in welchen Bereichen der Anwendung Optimierungspotential liegt. Die Hardware rückt
damit in den Hintergrund. Da alle Leistungen letztendlich von der Hardware erbracht werden, darf
sie aber nicht ignoriert werden. Es folgt eine Beschreibung des Testsystems: Das Testsystem ent-
hält einen Intel Pentium 4 Prozessor mit 1,6 GHz Taktung, dem 1024 Megabyte Arbeitsspeicher
zur Verfügung stehen. Das verwendete Betriebsystem ist die Linux Distribution Kubuntu mit dem
Linux-Kernel Version 2.6.24. Die verwendete Java Virtual Machine ist die von Sun Microsys-
tems entwickelte HotSpot VM. Sie wird in Version 1.6.0_06 und im „Client Modus“ (siehe 2.2.8)
eingesetzt. Der verwendete Web-Server ist Apache Tomcat in Version 6.0.18 (siehe nächsten Ab-
schnitt).
5.2.2 Einflüsse des Tomcat
Die Web-Anwendung JCommSy läuft in einem Servlet-Container (siehe 2.4.2), dem Apache Tom-
cat2. Zusätzlich zur JVM muss der Tomcat erst hochgefahren werden. Beispielsweise werden die
verwendeten JSPs in Servlets übersetzt, wodurch der erste Zugriff auf eine JSP langsam ist. Im
regulären Betrieb, fallen diese Aktivitäten nicht mehr auf, da sie im Vergleich zur normalen, täg-
lichen Benutzung nur selten passieren. Aus diesem Grund, wird für das Analysieren folgende
Prozedur festgelegt:
1. Der Tomcat wird neu gestartet. Um sicher zu gehen, dass der Tomcat fertig gestartet ist,
werden 2 Minuten eingeplant. Typische Startzeiten auf dem Testsystem liegen bei 25 bis 55
Sekunden.
2. Der Benchmark wird einmal komplett durchlaufen. Dabei wird auf alle JSPs bereits einmal
zugegriffen und sämtliche Klassen und Einstellungsdateien werden geladen.
3. Der Benchmarking-Durchlauf wird zweimal durchgeführt. Diese zwei Durchläufe werden
gemessen.
Diese Prozedur hilft, die Einflüsse durch die Startphase der Anwendung zu minimieren und mög-
lichst aussagekräftige Messungen zu erhalten.
5.2.3 Testszenario
In diesem Unterabschnitt wird das Testszenario vorgestellt, anhand dessen die Performance des
JCommSy untersucht wird. Das JCommSy kann in seinem derzeitigen Zustand noch nicht ohne
das PCommSy die volle Funktionalität des CommSy anbieten (siehe 2.3.3). Der An- und Ab-
meldevorgang wird komplett vom PCommSy übernommen. Die Rubriken Gruppen, Termine und
Ankündigung sind in Java umgesetzt und werden für die Messungen verwendet.
Es wurden Arbeitsschritte mit dem JCommSy herausgesucht und mehrere zusammenhängende
Arbeitsschritte mit einem Namen zu einem Testfall zusammengefasst. Gemeinsam ergeben die
2http://tomcat.apache.org/
5.2 Vorgehen 41
Testfälle das Benchmarking-Szenario, sie sind aber auch einzeln durchführbar. Ausgangspunkt je-
des Testfalls ist, dass der Benutzer angemeldet ist und einen Raum ausgewählt hat. Die Struktur
macht es möglich, bestimmten Testfällen mehr Gewicht zu geben, da einzeln spezifiziert werden
kann, wie oft ein Testfall ausgeführt wird. Es ist damit möglich, gezielt nur einen Testfall durch-
zuführen bzw. einen oder mehrere herauszunehmen.
Beim Zusammenstellen der Testfälle wurden folgende Aspekte in die Überlegungen mit einbezo-
gen: Die Testfälle sollen möglichst dem entsprechen, was Benutzer mit dem System machen und
Daten aus dem normalen Betrieb verwenden [Shi00, S. 13]. Es wurde versucht alle Funktionalitä-
ten des JCommSy in den Testfällen abzudecken. Dabei sollte das mehrmalige Abrufen der selben
Seite hintereinander in den Testfällen enthalten sein.
Folgende Testfälle wurden ausgearbeitet:
Übersichtsseiten simuliert das häufige Wechseln zwischen den Indexseiten. Es werden die an-
gegebenen Indexseiten in folgender Reihenfolge aufgerufen: Ankündigung, Termine, Grup-
pen, Gruppen, Termine, Ankündigung, Gruppen, Ankündigung.
Lesen beinhaltet das gezielte Auswählen von Einträgen und den Aufruf der zugehörigen Detail-
ansicht. Anschließend werden die nächsten 20 Einträge der Ankündigungsübersichtsseite
angefordert und eine Ankündigung ausgewählt. Abschließend wird in die Gruppenrubrik
gewechselt und dort eine Gruppe ausgewählt.
Bearbeiten enthält den Aufruf der Detail- und Bearbeitenseite einer Ankündigung, sowie den
Beitritt zu und Austritt aus einer Gruppe.
Erstellen/Löschen erstellt eine Ankündigung und speichert und löscht diese wieder. Anschlie-
ßend wird das gleiche in der Termine- und Gruppenrubrik gemacht.
Spezialfunktionen simuliert die Benutzung fortgeschrittener Funktionalitäten. Erst wird die An-
zahl der Einträge pro Seite auf 10 reduziert, anschließend werden alle vorhandenen Einträge
auf einer Seite dargestellt. Über die Suche-Funktionalität wird nach einer bestimmten Grup-
pe gesucht und diese angezeigt. Die Seite wird gespeichert und Informationen darüber, wer
die Seite zuletzt bearbeitet hat, werden abgerufen.
Sortieren sortiert die Übersichtsseiten nach unterschiedlichen Eigenschaften und in unterschied-
lichen Richtungen. Dies beinhaltet das Ändern der Sortierrichtung von absteigend nach auf-
steigend.
Die detaillierte Abfolge der einzelnen Schritte jedes Testfalls kann Anhang B entnommen wer-
den. In zwei der Testfälle, wird der Datenbestand geändert. Dabei wird darauf geachtet, dass der
Zustand, der nach dem Durchlauf vorliegt, dem davor entspricht. Eine erstellte Ankündigung wird
also wieder gelöscht. Im CommSy gelöschte Einträge werden als gelöscht markiert, verbleiben je-
doch in der Datenbank. Demzufolge verändert sich das System bei jedem Benchmark-Durchlauf
etwas. Die Datenbank enthält mehrere tausend, zum Teil gelöschte, Einträge. Durch die Profi-
lings wird sich die Größenordnung nicht ändern. Da die Größenordnung weit entfernt von den
42 Kapitel 5 Performance-Analyse
möglichen 50.000.000 Einträge [MyS06] liegt, wird davon ausgegangen, dass die Datenbankzeit
höchstens linear mit der Anzahl der Einträge steigt. Der Fehler wird deshalb vernachlässigbar
klein sein.
5.3 Software-Werkzeuge
Um die Performance-Analyse durchzuführen, werden Software-Werkzeuge benötigt. JMeter wird
eingesetzt, um einen immer wieder durchführbaren Benchmark zur Verfügung zu haben. Das Pro-
filing der Anwendung wird mit Softwareunterstützung durchgeführt. In dieser Arbeit wird der
JProfiler vorgestellt und eingesetzt.
5.3.1 JMeter
Um die Performance des Systems messen zu können, müssen die Funktionen, die gemessen wer-
den sollen, ausgeführt werden. Im Fall des JCommSy, also einer Web-Anwendung, heißt das, dass
eine oder mehrere HTTP-Anfragen an das System gerichtet werden müssen. Damit diese Testan-
fragen nicht jedesmal von einem Menschen mit einem Web-Browser manuell ausgelöst werden
müssen, wird dies automatisiert. Es gibt eine Reihe von Möglichkeiten, entsprechend gestaltete
HTTP-Anfragen zu generieren. Im JCommSy werden HttpUnit-Tests eingesetzt, um die Oberflä-
che und deren Funktionalität zu testen. Mit HttpUnit könnten Testszenarien geschrieben werden
und als Benchmark ausgeführt werden. Der Nachteil dieses Vorgehens ist, dass diese Anfragen in
Java geschrieben werden müssen. Die Navigation erfolgt, indem die HTML-Struktur der Antwort
nach einem bestimmten Link durchsucht und ein Klick auf diesen simuliert wird. Es müsste also,
wie in den Testfällen, ein Ablauf wie in Quelltext 5.1 programmiert werden.
1 ...
2 // change to rubric "Gruppen"3 _currentPage = _currentPage.getLinkWith(RUBRICGROUP).click();
45 // goto detail page of group "TEST_GROUP_ROOM"6 _currentPage = _currentPage.getLinkWith(TEST_GROUP_ROOM).click();
78 // ensure that HTTP_USER_NAME is member9 WebLink link = _currentPage.getLinkWith(HTTP_USER_NAME);
10 assertNotNull("http unit user is not listed", link);
11 ...
Quelltext 5.1: Typischer Aufbau eines Tests mit HttpUnit
Diese Art von Tests, wie sie im JCommSy eingesetzt werden, haben den Vorteil, dass sie sich gut
mit den anderen Komponententests integrieren und diese in Form von Akzeptanztests abrunden
(siehe [Lin05, S. 5 f.] für Definitionen der unterschiedlichen Arten von Tests). Diese Art zu testen
ist allerdings fehleranfällig, aufwendig und langsam. Bereits das Umbenennen von Links kann
dazu führen, dass die Tests nicht mehr erfolgreich durchlaufen. Andererseits kann man detailliert
5.3 Software-Werkzeuge 43
Abbildung 5.1: Ausschnitt aus der JMeter-Benutzungsoberfläche
überprüfen und dadurch sicherstellen, dass ein Link unter einem genau vorgegebenen Namen vor-
handen ist. Dies ist gerade im Fall des JCommSy von Bedeutung, da mit dem PCommSy eine
Vorlage vorhanden ist, die möglichst genau nachgebaut werden soll.
Um ein Szenario für ein Benchmark zu definieren, ist dieses Vorgehen umständlich und unnö-
tig. Bei einem Benchmark geht es nicht um die Struktur der ausgelieferten Seite, sondern darum
eine Abfolge von HTTP-Anfragen an das System zu definieren. Mit dem Werkzeug Apache JMe-
ter3 können über eine grafische Benutzungsoberfläche Testpläne zusammengestellt werden. Diese
bestehen aus einer Reihe von HTTP-Anfragen. Für jede einzelne Anfrage (oder mehrere gemein-
sam) besteht die Möglichkeit HTTP-Parameter, Zieladresse und weitere Eigenschaften zu setzen.
JMeter ermöglicht es, diese Anfragen zu gruppieren, wiederholt auszuführen, zufällige Pausen
einzufügen und die Antworten auszuwerten. Ein Ausschnitt der JMeter-Oberfläche ist in Abbil-
dung 5.1 zu sehen.
Darüber hinausgehend bietet JMeter Möglichkeiten, das Antwortverhalten zu beobachten und
zahlreiche Daten zu erfassen, z. B. die durchschnittliche Antwortzeit auf eine Anfrage. Diese Er-
gebnisse können grafisch oder tabellarisch aufbereitet angezeigt und protokolliert werden. Mit
Hilfe der „HTTP Proxy Server“-Funktionalität ist es möglich, mit einem herkömmlichen Web-
Browser durch eine Web-Seite zu navigieren und alle HTTP-Anfragen, die dabei ausgelöst wer-
den, von JMeter mitprotokollieren zu lassen. Beim CommSy können aufgrund von Einschrän-
kungen, die durch die Verwendung von Sitzungen entstehen, nicht einfach alte Anfragen erneut
3http://jakarta.apache.org/jmeter/
44 Kapitel 5 Performance-Analyse
gesendet werden. Mit wenigen Anpassungen können die aufgenommenen Anfragen allerdings
CommSy-kompatibel gemacht werden. Die einzelnen Anfragen können benannt und mit Kom-
mentaren versehen werden. Somit können sie einfach identifiziert, kopiert und wiederverwendet
werden. Die in der Abbildung 5.1 ausgewählte und angezeigte HTTP-Anfrage entspricht der, die
ausgelöst wird, wenn man auf die Gruppen-Rubrik klickt und fordert deren Index-Seite an.
5.3.2 JProfiler
In dieser Arbeit kommt der Profiler JProfiler4 von ej-technologies zum Einsatz. Mit dem JPro-
filer kann man in Java geschriebene Anwendungen untersuchen. Dafür wird mittels Bytecode-
Instrumentierung der auszuführende Bytecode verändert und entsprechend die Aufrufe protokol-
liert, Speicherbelegungen abgefragt und Ausführungszeiten ermittelt (siehe auch 2.2.7). Der JPro-
filer besteht aus zwei logischen Komponenten. Eine Komponente muss auf demselben Rechner
laufen, auf dem die zu analysierende Software läuft. Das Startskript der Anwendung wird so an-
gepasst, dass es der JVM den JProfiler als Profiling-Agent übergibt. Standardmäßig wartet diese
Komponente darauf, dass die JProfiler-Oberfläche sich mit ihr verbindet. Die Oberfläche ist die
zweite Komponente und kann auf einem anderen Rechner laufen. Dies hat z. B. den Vorteil, dass
die Arbeit mit der Oberfläche, den Prozessor auf der zu analysierenden Maschine nicht bean-
sprucht und damit die Ergebnisse nicht beeinflusst.
In der JProfiler-Benutzungsoberfläche erstellt oder lädt der Benutzer eine „Session“. Diese enthält
Informationen, wo die wartende Profiling-Komponente liegt, welche Profiling-Optionen aktiviert
sind und welche Klassen herausgefiltert werden sollen. Wird die Session gestartet, verbindet sich
die JProfiler-Oberfläche mit der Profiling-Komponente und übergibt dieser die gewählten Ein-
stellungen. Diese startet entsprechend die Anwendung. Der JProfiler erhält zu diesem Zeitpunkt
bereits Informationen über das laufende System und kann diese anzeigen.
Der JProfiler bietet fünf grundlegend unterschiedliche Ansichtenkategorien an, welche wiederum
unterschiedliche Ansichten enthalten.
Die „Memory Views“ liefern u. a. Informationen von welcher Klasse es am meisten Objekte gibt,
welche Methoden am meisten Objekte erzeugen und wo innerhalb der Anwendung Objekte einer
Klasse erzeugt werden.
Der „Heap Walker“ erlaubt es, ein Speicherabbild der Anwendung zu erstellen und dieses genauer
zu analysieren. Dafür werden Informationen extrahiert, wie z. B. von welcher Klasse wie viele
Objekte existieren. Man kann Informationen darüber erhalten, welche Objekte welche anderen
Objekte referenzieren und auf diese Weise durch den Speicher navigieren.
Die „CPU Views“ liefern Informationen darüber, in welchen Methoden das Programm wieviel
Zeit verbringt. Dabei kann der JProfiler die Informationen auf unterschiedliche Arten darstellen
und in einer Hotspots-Ansicht mit der Anzahl der Aufrufe zusammenbringen.
In den „Thread Views“ werden Informationen zu den einzelnen Threads dargestellt. Welcher
Thread wartet auf welchen Monitor, welcher Thread ist gerade aktiv, wo kommt es zu einem
4http://www.ej-technologies.com/products/jprofiler/overview.html
5.3 Software-Werkzeuge 45
Abbildung 5.2: Benutzungsoberfläche des JProfiler
Deadlock – dies sind Fragen, bei deren Beantwortung diese Ansichten helfen.
Die „VM Telemetry Views“ informiert über die aktuelle Speichersituation der JVM, darüber wann
der Garbage Collector aktiv wird, wieviele Klassen geladen sind und wie groß die Gesamtauslas-
tung des Prozessors ist.
Die Benutzungsoberfläche des JProfiler ist in Abbildung 5.2 zu sehen. Die dort abgebildete An-
sicht zeigt, welche Klassen wieviel Prozent der Ausführungszeit benötigen, wie lange ihre Metho-
den durchschnittlich ausgeführt werden und wie oft ihre Methoden aufgerufen werden.
Der JProfiler bietet die Möglichkeit Filter zu spezifizieren, die festlegen welche Klassen in die
Messungen mit einbezogen werden. Ohne Filter würden die Klassen der Bibliotheken, beispiels-
weise java.lang.String, mit betrachtet werden. Dies hätte zur Folge, dass die Ausgabe sehr lang
und damit unübersichtlich würde. Außerdem interessiert es Anwendungsentwickler selten wie per-
formant die Methoden aus der Standardbibliothek sind, da sie keinen Einfluss darauf haben. Ein
weiterer Aspekt, der gegen das Profiling aller Klassen spricht, ist, dass dadurch die gesamte An-
wendung signifikant langsamer laufen würde. Durch das Profiling entsteht ein Mehraufwand, der
mit der Anzahl der betrachteten Klassen zunimmt.
Mit einem Filter lässt sich beispielsweise spezifizieren, dass nur die Klassen aus dem Package
de.unihamburg.informatik.jcommsy und allen darin enthaltenen Packages5 betrachtet werden.
Alle anderen Klassen werden dann von JProfiler nicht gemessen. Allerdings werden alle Auf-
5Das Enthaltensein ist über die Namensstruktur von Packages definiert (siehe 2.1.1).
46 Kapitel 5 Performance-Analyse
rufe aus einer nicht gefilterten Klasse immer mit aufgeführt. Dies wird an folgendem Beispiel
verdeutlicht. Angenommen die Klasse Announcement wird gemessen, aber java.util.Date und
java.lang.String sind durch einen Filter ausgeschlossen. Weiter angenommen, Announcement
ruft an Date die Methode toString auf, dann wird diese Methode noch von JProfiler mit ihren
Zeiten aufgeführt. Die Tatsache, dass toString bei der Ausführung noch eine Methode an String
aufruft, wird vom Profiler nicht mehr registriert.
Mit Hilfe von Triggern (Auslösern), kann der Profiler so eingestellt werden, dass er automatisch
auf bestimmte Ereignisse reagiert. Auslösende Ereignisse können z. B. der Aufruf einer Metho-
de oder das Übersteigen eines bestimmten Speicherfüllstands sein. Über Aktionen kann definiert
werden, wie der JProfiler beim Eintreten des Ereignisses reagiert. Beispielsweise könnte ein Spei-
cherabbild erstellt werden oder der Speicherfüllstand in die Log-Datei geschrieben werden. Um
das Starten der Profiling-Aktivitäten auf einen Zeitpunkt nach dem Start des Tomcat zu verschie-
ben, wird ein zeitlicher Trigger verwendet, der nach zwei Minuten die Aktion „Start recording“
auslöst.
5.3.3 Alternativen
Neben dem hier eingesetzten JProfiler gibt es weitere Profiler für Java-Anwendungen. Ähnlichen
Funktionsumfang wie der JProfiler haben JProbe6 und YourKit7. Alle drei ermöglichen das Mes-
sen der Ausführungszeiten der einzelnen Methoden und unterstützen das Finden von Hotspots
(siehe 2.2.7). Das Speichermanagement wird von allen beobachtbar gemacht und es gibt Unter-
stützung beim Finden von Speicherlecks. Alle drei unterstützen sowohl Java 5 als auch Java 6 und
lassen sich in Entwicklungsumgebungen einbinden, insbesondere in die im Rahmen des JComm-
Sy eingesetzte Entwicklungsumgebung Eclipse8.
Das von Quest Software entwickelte JProbe erlaubt, neben dem Profiling, die Testabdeckung zu
messen. Somit kann festgestellt werden, welche Quelltextzeilen während der Komponententests
nicht durchlaufen und somit nicht getestet werden. Im JCommSy kommt dafür Cobertura9 zum
Einsatz. Dieser Aspekt der Softwarequalität wird in dieser Arbeit jedoch nicht betrachtet.
Die Software YourKit stellt neben einem Java Profiler auch einen Profiler für die .NET Umge-
bung zur Verfügung. Insbesondere wenn absehbar nicht nur Java Anwendungen analysiert werden
sollen, kann durch die Verwendung von YourKit eine doppelte Einarbeitung vermieden werden.
YourKit ermöglicht Profilings von Anwendungen, die mittels Java Micro Edition (J2ME) für mo-
bile Geräte entwickelt sind.
Zu JMeter gibt es ebenfalls Alternativen. Die Anwendung The Grinder10 ermöglicht das Erstellen
von Lasttests für unterschiedliche Schnittstellen. Neben Remote Method Invocation (RMI), Web-
6http://www.quest.com/jprobe/7http://www.yourkit.com/8http://www.eclipse.org/9http://cobertura.sourceforge.net/
10http://grinder.sourceforge.net/
5.4 Betrachtungsebenen 47
Services und weiteren, können insbesondere HTTP-Anfragen generiert werden. Diese können be-
nutzt werden, um das Verhalten von Web-Servern bei steigenden Anfragezahlen zu untersuchen.
5.4 Betrachtungsebenen
Die Architektur einer Software ist nicht ohne weiteres aus der Quelltext-Repräsentation heraus-
zulesen. Aus dem Quelltext wird zwar deutlich, welche Klasse zu welchem Package gehört, aber
bereits die Zuordnung zu einer Schicht, ist nicht mehr ersichtlich. Im Gegensatz dazu, findet man
einen Programmabschnitt, der in einer Schleife eine Reihe von mathematischen Operationen aus-
führt, direkt im Quelltext wieder. Es gibt also unterschiedliche Ebenen, auf denen man eine Soft-
ware betrachten und analysieren kann. Dies gilt auch für die Performance-Analyse. Eine Schleife
findet sich direkt im Bytecode wieder und kann mit einem Profiler gemessen werden. Für einen
Profiler existieren keine Schichten und es gibt keine Zuordnung der Klassen zu einer Schicht.
Dementsprechend ist es schwieriger die Performance auf die Architektur-Ebene abzubilden. Zwi-
schen diesen beiden Ebenen liegt noch die Ebene des Zusammenspiels verschiedener Klassen.
Auf Muster bezogen werden die drei Ebenen, als Architekturmuster, Entwurfsmuster und Idiome
bezeichnet [RH06, S. 342 ff.].
Die gröbste Ebene, die in dieser Arbeit eingenommen wird, ist die Architekturebene. Die einzel-
nen Artefakte sind dann Architekturelemente, wie z. B. eine Schicht. Es stellt sich die Frage, ob
die Verwendung einer Schichtung Auswirkungen auf die Performance hat, und wenn ja, welche.
Da diese Ebene nicht direkt gemessen werden kann, muss entsprechend aggregiert werden.
Auf der Ebene der Entwurfsmuster wird das Zusammenspiel verschiedener Klassen in den Mittel-
punkt gerückt. Durch die Verwendung von Entwurfsmustern (siehe [GHJV95]) wird die Flexibi-
lität des Systems erhöht. Viele Entwurfsmuster basieren darauf, dass zusätzliche Objekte benötigt
werden und die Aufgaben durch Delegation umverteilt werden. Im JCommSy wird beispielsweise
eine Composite-Struktur für die Fragmente und Ausstatter genutzt. Die Verwendung von Ent-
wurfsmustern hat Auswirkungen auf den benötigten Speicher und die zeitliche Performance der
Anwendung. Demgegenüber steht eine klarer strukturierte Anwendung, deren Quelltext sich bes-
ser kommunizieren und verändern lässt. Entwurfsmuster betreffen in der Regel einzelne Subsys-
teme und sind unabhängig von der Anwendungsdomäne.
Auf feinster Ebene besteht die Möglichkeit den Quelltext innerhalb einer Methode oder Klasse zu
optimieren, z. B. indem eine häufige Konkatenation von Strings mittels Plus-Operator vermieden
wird [BGWA08, S. 227]. Die Java-Klasse StringBuilder ersetzt dies performanter. Die Lesbar-
keit des Quelltextes wird dadurch beeinflusst. Eine Optimierung auf dieser Ebene ändert nur eine
einzelne Methode und hat keine Auswirkungen auf andere Methoden (siehe [Shi00] und [Bul00]).
Im Gegensatz zu den beiden anderen Ebenen, haben die Idiome einen direkten Bezug zu konkreten
Problemsituationen der Entwickler und sind deshalb häufig an eine Anwendungsdomäne und/oder
eine Programmiersprache gebunden. Analog sind Optimierungen auf dieser Ebene spezifisch für
die Anwendungsdomäne und die Programmiersprache.
48 Kapitel 5 Performance-Analyse
Alle drei Ebenen werden in dieser Arbeit betrachtet. Um sie etwas klarer abzugrenzen, wird im
Folgenden für jede eine Reihe von Ansatzpunkten aufgeführt. Um die Performance-Skala fassba-
rer zu machen, werden zunächst Performance-Kategorien eingeführt und die Bausteine der Pro-
grammierung diesen zugeordnet.
5.4.1 Performance-Kategorien und -Bausteine
Während der Untersuchungen konnten Aspekte in unterschiedlichen zeitlichen Größenordnungen
identifiziert werden. Um diese handhabbar zu machen, werden fünf Kategorien eingeführt.
Die kleinsten von Profilern erfassten Einheiten sind Methoden. Sie setzen sich aus einfachen Java-
Konstrukten, wie Zuweisungen oder elementaren Berechnungen, zusammen. Diese bilden die ers-
te Kategorie auf der Performance-Skala. Einfache Methoden, wie Setter- oder Getter, fallen eben-
falls in diese Kategorie.
Standardmethoden können bereits von Profilern erfasst werden und bauen auf den elementaren
Konstrukten auf. Sie enthalten Funktionalitäten die Entwicklern ähnlich elementar erscheinen wie
die Java-Konstrukte.
Darauf aufbauend sind Schleifen und Rekursionen als Konstrukte aufgefallen. Schleifenkörper
enthalten wiederum Elemente der beiden kleineren Kategorien. Da Schleifen in der Regel dafür
gemacht sind, häufig durchlaufen zu werden und nicht nur Kategorie I-Aspekte enthalten, bilden
sie Kategorie III.
Ein- und Ausgabe-Operationen, wie Festplattenzugriffe oder Netzwerkkommunikationen, bilden
die nächste Kategorie, die sich wiederum durch deutlich größere Zeiten auszeichnet.
Als Kategorie V wurden Algorithmen identifiziert. Sie bestehen aus Elementen der unteren Kate-
gorien und bringen diese zu einem komplexen Gebilde zusammen, dass als Einheit wahrgenom-
men wird.
Zusammengefasst ergeben sich folgende fünf Kategorien.
I Java-Konstrukte: 10 Nanosekunden
II Standardmethoden: 1 Mikrosekunde
III Schleifen: 100 Mikrosekunden
IV Ein-/Ausgabe: 10 Millisekunden
V Algorithmen: > 100 Millisekunden
Die angegebenen Größen stellen den Mittelpunkt der Kategorien dar, wobei sich eine Kategorie
über den Bereich von 2 Zehnerpotenzen erstreckt (siehe Abbildung 5.3). Beispielsweise reicht Ka-
tegorie I von einer bis zu 100 Nanosekunden. Die Einteilungen sind nicht immer eindeutig. Eine
Schleife, die nur einmal durchlaufen wird, kann auch in Kategorie I oder II fallen. Besonders die
Kategorie V ist unscharf, da die Ausführungszeiten von Algorithmus zu Algorithmus unterschied-
lich sind. Die Einteilung soll nicht aussagen, dass es keine Algorithmen gibt, die in Kategorie II
fallen, sondern sie sagt aus, dass Algorithmen durchaus Zeiten im Bereich von 100 Millisekunden
5.4 Betrachtungsebenen 49
erreichen können.
Die folgende Aufzählung enthält grundlegende Bausteine mit ihren Beschreibungen und ordnet
sie den Kategorien zu. Die Bausteine sind nicht unabhängig voneinander, da die komplexeren
die einfacheren einsetzen und beinhalten. Sie werden vom Entwickler als eigenständige Einhei-
ten wahrgenommen und deswegen hier aufgeführt. Die Bausteine werden in die fünf Kategorien
eingeordnet:
Kategorie I:• Anweisung: Anweisungen, wie das Zuweisen einer Variablen oder die Addition zweier pri-
mitiver Datentypen, sind die kleinsten Bausteine.
• Sprung/Bedingung: Das Auswerten einer bedingten Anweisung oder ein Sprung, wie er in
einer Schleife benötigt wird.
• Methodenaufruf: Der Aufruf einer Methode, inklusive der Manipulation des Stacks.
• Objekterzeugung: Die Allokation von Speicher, z. B. das Erzeugen eines neuen Objekts.
• Indirektion: Das Erzeugen eines Objekts und das Aufrufen einer Methode an diesem.
Kategorie II:• Standard-Methode: Standard-Implementierungen von equals bzw. toString, wie sie z. B.
String bzw. Object vorgeben. Diese Methoden enthalten Algorithmen, befinden sich jedoch
sowohl aus Sicht des Entwicklers, als auch aus Performance-Sicht auf einer anderen Ebe-
ne als vom Entwickler selbst implementierte komplexe Algorithmen. Standard-Methoden
können vom Entwickler nicht verändert werden und bilden die Kategorie II.
Kategorie III:• Schleife/Rekursion: Abhängig von der Anzahl der Durchläufe, unterliegen sie einer breiten
Schwankung. Eine Schleife in der 10.000 Objekterzeugungen stattfinden landet in der Kate-
gorie III. Schleifen und Rekursionen enthalten in der Regel Kategorie II-Aspekte und wer-
den vielfach durchlaufen. Deshalb sind sie eine Kategorie höher als die Standard-Methoden
einzuordnen.
• Ein-/Ausgabe-Operation auf Dateien im Cache: Lese- und Schreibzugriffe auf Dateien
die sich im Cache befinden.
• lokale Socketverbindung: Socketverbindungen zum eigenen Rechner, z. B. um mit einem
anderen Prozess zu kommunizieren.
Kategorie IV:• Dateizugriff: Der Zugriff auf eine Datei auf der Festplatte, die nicht im Cache liegt.
• Datenbankanfrage: Zugriff auf eine Datenbank.
• Socketverbindung: Verbindung zu einem anderen Rechner im Netzwerk.
50 Kapitel 5 Performance-Analyse
Kategorie V:• Algorithmus: Ein Algorithmus der aufwendige Berechnungen durchführt und/oder die obe-
ren Aspekte beinhaltet.
Eine detailliertere Beschreibung der Ausführungszeiten der Bausteine ist Abbildung 5.3 zu ent-
nehmen. Diese Daten wurden mit Mikro-Benchmarks erhoben, die mittels einer angepassten Ver-
sion von JFreeProfilingCenter11 durchgeführt wurden. Auch wenn die exakten Messwerte und die
genaue Position der Kategorien auf unterschiedlichen Rechnern verschoben sind, so bleibt die
prinzipielle Einteilung und Unterteilung in die Kategorien bestehen.
1 10 100 1.000 10.000 100.000 1.000.000 10.000.000 100.000.000 [ns]Ausführungszeiten in Nanosekunden (logarithmische Darstellung)
einz
elne
Anw
eisung
enMetho
dena
ufruf
Objekterzeu
gung
Entwurfsmuster/zu
sätzliche
Indirektion
Equa
ls-Vergleich
vonzw
eiSt
ring
s
toSt
ring
vonOb
ject
Synchron
isation(2
Thread-Wechsel)
Schreibe
nin
eine
gecachte
Datei
Schleife/R
ekursion
:10
.000
Objekte
erzeug
enSo
cketverbindu
ng(selbe
rRechn
er)
JDBC
Datenba
nkan
frag
e(selbe
rRechn
er)
Socketverbindu
ng(L
AN)
Zugriff
aufObjekte
ausDB
über
Hiberna
teLe
seneine
run
gecachtenDatei
Sortiereneine
s1.00
0.00
0int-Arrays
IJava-Konstrukte
IIStandardmethoden
IIISchleifen
IVEin-/Ausgabe
VAlgorithmen
Nanosekunden Mikrosekunden Millisekunden
Abbildung 5.3: Gegenüberstellung von Ausführungszeiten und Einteilung in die Kategorien
Bevor die Ergebnisse der Analyse vorgestellt werden, werden zunächst die Betrachtungsebenen
detaillierter vorgestellt.
5.4.2 Innerhalb einer Klasse
Bereits innerhalb der untersten Betrachtungsebene können unterschiedliche Arten von Optimie-
rungen gemacht werden. Vorschläge für Aspekte, die untersucht werden können, fangen bei ele-
mentaren Anweisungen an und enden auf der Ebene des Entwurfs von Klassen. Unter dem Be-
griff „strength reduction“ versteht man die Verwendung schnellerer Befehle, z. B. kann x = x + 5
durch x += 5 ersetzt werden (siehe [Shi00, S. 76]). Eine Gegenüberstellung der Performance des
ternären Operators12 mit einem „if-then-else“-Konstrukt ist eine weitere Möglichkeit, auf dieser
feinsten Ebene, Performance zu betrachten. Diese Aspekte betreffen die Kategorie I.
Das Verwenden alternativer Algorithmen wird in der Regel auf dieser Ebene anzuordnen sein,
11http://sourceforge.net/projects/jfreeprofcenter/12Der Bedingungsoperator A ? B : C hat 3 Operanden und liefert B, falls A wahr ist und sonst C.
5.4 Betrachtungsebenen 51
kann jedoch auf der nächst höheren Ebene betrachtet werden. Dies passiert z. B. wenn unterschied-
liche Algorithmen als unterschiedliche Strategien nach Gamma [GHJV95] realisiert werden und
so zur Laufzeit auswechselbar sind.
Das häufige Konvertieren von Daten kann zu Performance-Problemen führen. Wird bei jedem Zu-
griff z. B. zwischen String und Integer konvertiert, kann untersucht werden, ob die Klasse nicht
anders hätte entworfen werden können.
Ein weiterer Aspekt dieser Ebene betrifft die Verwendung von Bibliotheken. Durch die korrekte
Benutzung der richtigen Klassen und Methoden kann die Performance gesteigert werden. Bei-
spielsweise stellt Bulka fest, dass die Verwendung von String.equalsIgnoreCase mehr als eine
Zehnerpotenz schneller ist, als wenn erst String.toLowerCase und dann String.equals benutzt
wird [Bul00, S. 10]. Alle drei Methoden sind in Kategorie II einzuordnen und die Verbesserung
also innerhalb einer Kategorie.
5.4.3 Zusammenspiel von Klassen
Diese Betrachtungsebene ist, wie die anderen beiden auch, nicht nur in der Objektorientierung zu
finden. Im Rahmen dieser Arbeit werden andere Programmierparadigmen jedoch nicht betrachtet.
Objektorientierung resultiert in einem Netz von Objekten mit vielen kleinen Methoden. Dabei
wird insbesondere Polymorphismus eingesetzt und damit zur Laufzeit ermittelt, welche Methode
aufgerufen werden soll. Dies verursacht Kosten, insbesondere, da die Prozessoren für statisch fest-
gelegte Methodenaufrufe optimiert sind [Shi00, S. 2]. Auf dieser Ebene kann betrachtet werden,
inwiefern sich die zusätzlichen Klassen und Methoden eines Entwurfsmusters auf die Performance
der Anwendung auswirken. Generell lässt sich nicht sagen, dass Objektorientierung oder Verer-
bung schlecht oder gut für die Performance sind. Es hängt davon ab, wie sie eingesetzt werden.
Richtig eingesetzt, kann Polymorphie z. B. „if-else“-Konstrukte unnötig machen und damit die
Performance steigern [Bul00, S. 91]. Die Verwendung von Interfaces und die Implementierung
gegen Interfaces ermöglicht es, die Implementierung zu kapseln und so austauschbar zu halten.
Die Verwendung von Fabrikmustern [GHJV95] zur Erzeugung von Objekten resultiert in zusätzli-
chen Laufzeitkosten (siehe [Shi00, S. 378]). Wie groß diese Kosten sind, bzw. an welchem Punkt
es sich lohnt auf Entwurfsmuster zu verzichten, wird auf dieser Ebene betrachtet und untersucht.
Objekte können einen Zustand haben und diesen ändern. Wenn ein Objekt zustandslos ist oder
seinen Zustand nicht ändern kann (also unveränderlich ist), dann kann das Objekt wiederverwen-
det werden und muss nicht neu erzeugt werden. Bei veränderlichen Objekten ist es eine wichtige
Entwurfsentscheidung, ob ein Objekt wiederverwendet wird oder ein neues Objekt erzeugt wird.
Letzteres führt in der Regel zu besser lesbarem Quelltext und vermeidet Fehler.
5.4.4 Architekturelemente
Auf der Architekturebene gibt es unterschiedliche Ansätze, Performance zu betrachten. Die direk-
ten Auswirkungen durch die Regeln einer Architektur sind ein Betrachtungspunkt. In einer strikten
Schichtenarchitektur dürfen Klassen einer Schicht nicht an einer anderen Schicht vorbei auf wei-
52 Kapitel 5 Performance-Analyse
ter unten liegende Schichten zugreifen. Im einfachsten Fall mag es nötig sein, in der zwischen
zwei Schichten liegenden Schicht eine Klasse anzubieten, die die Anfrage weiter durchreicht. Das
Resultat dieses Vorgehens ist ein zusätzlicher Methodenaufruf. Die Auswirkungen auf die Perfor-
mance können untersucht und bewertet werden.
Eine weitere Herangehensweise ist das Aggregieren von Performance-Informationen auf Archi-
tekturebene. Es kann ermittelt werden, welche Subsysteme welche Anteile der gesamten Ausfüh-
rungszeit einer Anwendung benötigen und wieviel Speicher dabei jeweils angefordert wird. Diese
Herangehensweise bietet die Möglichkeit, den Bereich, in dem die Anwendung optimiert wer-
den soll, einzugrenzen, um z. B. gezielt eine Bibliothek durch eine andere zu ersetzen. Es können
Informationen gewonnen werden, die es erlauben Architekturregeln zu überdenken oder neue ein-
zuführen. Erst wenn bekannt ist, wie das Laufzeitverhalten einzelner Komponenten ist, können
die Auswirkungen einer Aufteilung der Komponenten auf unterschiedliche Rechner abgeschätzt
und bewertet werden. Dies kann auf Grundlage der eingeführten Kategorien passieren. Wenn die
Anwendungslogik die meiste Zeit auf die Datenbank wartet, dann bringt es kaum Geschwindig-
keitsvorteile die Anwendungslogik auf mehreren Rechnern ausführen zu lassen.
Zu der Architektur einer Anwendung kann die Festlegung von Zuständigkeiten gehören. Werden
Services per Dependency Injection (siehe 2.4.6) in den sie benutzenden Klassen verfügbar ge-
macht, dann kann untersucht werden, was dieser Mechanismus an Performance kostet oder bringt.
Dependency Injection kann z. B. verhindern, dass die Services öfter als nötig erzeugt werden und
erst dann, wenn sie benutzt werden. Wird dieser Mechanismus umgangen, hat das Auswirkungen
auf die Performance und die Architektur.
Regeln für die Benutzung einer Datenbank haben unmittelbar Auswirkungen auf die Performance.
Die Verwendung eines objektrelationalen Mappers kapselt einige Aspekte, andere hingegen müs-
sen weiterhin beachtet werden. Es sollte versucht werden, die Zeit, die in einer Transaktion ver-
bracht wird, zu minimieren und z. B. lange Transaktionen in mehrere kurze aufzuteilen [Shi00,
S. 368]. Auch wenn ein OR-Mapper Verbindungen zur Datenbank bereithält und wiederverwen-
det, muss beachtet werden, wie dieser zu benutzen ist, um das unnötige Öffnen und Schließen von
Verbindungen zu verhindern.
Auf der Architekturebene werden auch Nebenläufigkeit und Parallelität betrachtet. Durch die ne-
benläufige Ausführung kann die Performance gesteigert werden, jedoch wird dadurch die Software
und deren Entwicklung komplexer. Es ist schwierig Nebenläufigkeit nachträglich hinzuzufügen.
Sie sollte von Anfang an mit eingeplant werden und in den Bereichen, in denen sie nicht erwünscht
ist, durch Synchronisation verhindert werden [Shi00, S. 370].
Das Verhalten der Anwendung unter Last und ihre Fähigkeit zu skalieren sind zwei weitere Aspek-
te von Performance. Sie beziehen sich stets auf das Zusammenspiel der Subsysteme, betreffen das
System als ganzes und können nur auf der Architekturebene betrachtet werden. Sie werden in
dieser Arbeit nicht behandelt.
5.5 Performance des JCommSy 53
5.5 Performance des JCommSy
Die bisherigen Abschnitte dieses Kapitels haben das Vorgehen bei einer Performance-Analyse be-
schrieben und die dafür nötigen Werkzeuge eingeführt. Es wurden die Ebenen definiert, auf denen
Performance Aspekte betrachtet werden können. Zusätzlich wurden fünf Performance-Kategorien
eingeführt.
In diesem Abschnitt werden die Ergebnisse verschiedener Aspekte der Performance des JComm-
Sy vorgestellt. Grundlage dafür ist die Version des JCommSy zu Beginn dieser Arbeit. Wie bei der
Architekturanalyse, leiten sich aus dieser Analyse Optimierungsmöglichkeiten ab. Diese Optimie-
rungen werden im nächsten Kapitel beschrieben, durchgeführt und deren Ergebnisse analysiert.
Die ersten Messungen am JCommSy lieferten das Ergebnis, dass die Anwendung 28 Prozent der
Zeit in dem Package apache.log4j verbringt. Dieses Package stellt Protokollierungsfunktionali-
tät in Form von „Loggern“ zur Verfügung. Im JCommSy wird das Logging an vielen Stellen zur
Ausgabe von Debug-Informationen verwendet. Beispielsweise wird in den Logger geschrieben,
ob ein Benutzer angemeldet ist. Der Logger ist so eingestellt, dass er die Informationen in einer
Datei auf der Festplatte ablegt, wodurch die langen Ausführungszeiten entstehen. Für den realen
Betrieb wird das Logging in der Regel auf Ausgaben der Stufe Warnung oder höher eingestellt
sein. Konfiguriert man den Logger entsprechend, benötigt der Logger nur noch vernachlässigbar
wenig Zeit. Für alle weiteren Tests, wird der Logger auf der Stufe Warnung verbleiben.
5.5.1 Zeitliche Performance
In diesem Abschnitt wird die zeitliche Performance des JCommSy untersucht und dargestellt.
Zunächst passiert dies aus Sicht eines Benutzers, anschließend detaillierter mittels Profiling auf
dem Server. Die Ergebnisse werden auf Ebene der Schichtenarchitektur dargestellt. Analysiert
wird hier das JCommSy in der Version von Oktober 2008 und somit in dem Zustand vor den
durchgeführten Optimierungen.
Messung beim Client
Zunächst einmal werden die durchschnittlichen Antwortzeiten dargestellt, wie JMeter sie auf-
zeichnet. Diese Zeiten wurden gewonnen, ohne das ein JProfiler nebenbei die Anwendung misst.
Die Ergebnisse entsprechen in einer Näherung dem, was der Benutzer des JCommSy wahrnehmen
würde, bis auf die Tatsache, dass das eigentliche Darstellen des Ergebnisses im Browser zusätzlich
Zeit benötigt. Die Messung zeigt, dass Anfragen, die eine Detail- oder Editieransicht betreffen, bei
ca. 260 bzw. 120 ms liegen. Das Speichern und Erzeugen von neuen Ankündigungen, Gruppen und
Terminen gehört in die Editieransicht-Kategorie. Die Indexseiten brauchen mit ca. 400 bis 700 ms
deutlich länger. Hierbei fällt auf, dass Ankündigungen mit ca. 700 ms deutlich langsamer sind, als
Gruppen mit ca. 410 ms. Zu den langsamen Aktionen gehört das Löschen von Einträgen (ca. 620
ms), sowie das Sortieren der Listen mit ca. 800 ms. Über alle Anfragen des Benchmarks gemit-
telt, ergibt sich ein Durchschnitt von ca. 400 ms pro Anfrage. Detaillierte Ergebnisse können im
Anhang in Abbildung A.1 gefunden werden.
54 Kapitel 5 Performance-Analyse
Schicht Zeit in ms Anteil in %service 143.990 ms 35,13 %util 141.000 ms 34,40 %presentation 83.682 ms 20,41 %item 35.798 ms 8,73 %domainvalue 3.889 ms 0,95 %migration 1.566 ms 0,38 %logic 0 ms 0 %Gesamt 409.925 ms 100 %
Tabelle 5.1: Laufzeiten der Schichten (Stand: 14. Oktober 2008)
Laufzeiten pro SchichtDie folgende Darstellung gibt Aufschluss darüber, in welchen Schichten des JCommSy welche
Anteile an Zeit verbraucht werden. Da die Schichten nicht dem Quelltext oder dem Bytecode ent-
nommen werden können, kann der JProfiler nicht automatisch auf diese Ebene aggregieren. Die
Messungen wurden deshalb auf Package-Ebene aggregiert und anschließend die Packages ma-
nuell den Schichten zugeordnet. Einer Schicht wird hierbei nicht nur die Zeit zugeordnet, die in
ihren JCommSy Klassen verbracht wird, sondern auch die Zeit, die in aufgerufenen Fremdklassen
verbracht wird. Die Service-Schicht enthält beispielsweise die Zeit, die Hibernate benötigt. Die Er-
gebnisse sind in Tabelle 5.1 dargestellt. Mit 35 Prozent benötigt die Service-Schicht am längsten.
Dies beinhaltet die Datenbankzugriffe. Die Util-Schicht landet mit 34 Prozent an zweiter Stelle.
Die Präsentations- und die Items-Schicht bilden mit 20 bzw. 9 Prozent das Mittelfeld. Die Schicht
Domainvalue und Migration sind in dem derzeitigen Zustand fast bedeutungslos für die Ausfüh-
rungszeit des JCommSy. Eine Sonderrolle nimmt die Logik-Schicht ein, da sie zu diesem Zeit-
punkt noch keine Klasse enthielt. Insgesamt summieren sich die Ausführungszeiten der Schichten
zu 409 Sekunden, was bei 102 Anfragen ca. 4019 ms pro Anfrage ergibt. Dieser Wert ist nicht
direkt mit den Ergebnissen des JMeters zu vergleichen, da durch die vom Profiler durchgeführte
Instrumentierung und Protokollierung ein erheblicher Mehraufwand entsteht. Am JCommSy und
mit dem hier verwendeten Testsystem, kann der Profiler die Ausführung um Faktor 10 verlangsa-
men.
Die lange Ausführungszeit der Util-Schicht wird durch den PHPDeserializer verursacht. Eine
Optimierung und die entsprechenden Ergebnisse sind in Abschnitt 6.1.1 zu finden. Die Zeiten der
Präsentationsschicht werden durch die JSPs ausgemacht und nicht optimiert.
Einen großen Teil der Ausführungszeit verbringt das JCommSy im verwendeten Rahmenwerk Hi-
bernate. Das lässt sich damit begründen, dass Hibernate für den Datenbankzugriff verantwortlich
ist. Dies beinhaltet die Verwaltung von Datenbankverbindungen, das Erzeugen und Abschließen
von Transaktionen sowie die Zeit, die die Datenbank für das Bereitstellen der Daten braucht. Im
schlimmsten Fall muss die Datenbank die angeforderten Daten von der Festplatte lesen. Alles
das summiert sich zu der Zeit auf, die vom JProfiler Hibernate zugeordnet wird, welches aus der
Service-Schicht benutzt wird. Innerhalb der Service-Schicht wird der Großteil der Zeit durch die
5.5 Performance des JCommSy 55
Schicht Speicher Anteil Anzahl an Allokationenin MB in Prozent in Tausend
service 368 MB 30,35 % 11016util 423 MB 34,88 % 4736presentation 381 MB 31,46 % 4498item 32 MB 2,66 % 1311domainvalue 2 MB 0,16 % 74migration 6 MB 0,49 % 91logic 0 KB 0 % 0Gesamt 1212 MB 100 % 21726
Tabelle 5.2: Speicherallokationen pro Schichten (Stand: 14. Oktober 2008)
Klasse FileServiceHibernate (siehe 6.1.4) und Zugriffe aus den IncludeTags verursacht (siehe
6.1.3).
5.5.2 Speicherverbrauch
In diesem Abschnitt wird der Speicherumsatz des JCommSy aus dem Blickwinkel der Schich-
tenarchitektur betrachtet. Speicherumsatz bezeichnet im Folgenden die Menge an Speicher, die
während des gemessenen Zeitraums für Objekterzeugung angefordert wurde. Ein Speicherumsatz
von 10 Megabyte (MB) bedeutet nicht, dass zu einem Zeitpunkt 10 MB Arbeitsspeicher benötigt
werden, sondern nur, dass in dem betrachteten Zeitraum insgesamt 10 MB Speicher angefordert
wurden.
Wie schon bei der Messung der Zeiten, hat der JProfiler keine Möglichkeit, die Daten auf die Ebe-
ne der Schichtung zu aggregieren. Es wird abermals auf Package-Ebene gemessen und von Hand
aggregiert.
Die Speicheranforderungen von gefilterten Klassen werden zu den aufrufenden Klassen hinzu-
gezählt, wie dies auch schon bei den Zeit-Messungen gemacht wurde. Ein Domainvalue erzeugt
beispielsweise ein Calendar-Objekt und ruft an dem Methoden auf, welche Strings und Dates
erzeugen. Gehört Calendar bereits zu den ausgefilterten Klassen, dann enthält Domainvalue trotz-
dem alle die Speicheranforderungen von Domainvalue, Calendar und auch String und Dates die
bei Domainvalues Benutzung von Calendar passiert sind. Die Ergebnisse der Messungen sind in
Tabelle 5.2 dargestellt.
Die meisten Objekterzeugungen und der größte Speicherumsatz passieren in den Util-, Service-
und Präsentationsschichten. Die Objekte der Präsentations- und Util-Schicht sind dabei durch-
schnittlich deutlich größer als die der Service-Schicht. Während die Service-Schicht z. B. die
fachlichen Items erzeugt, liegen in der Präsentationsschicht die JSP-Dateien, welche mit Strings
umgehen müssen. Diese werden benötigt, um die gewünschte HTML-Seite zusammenzustellen.
Innerhalb der Util-Schicht fordert der PHPDeserializer den Speicher an. Auch diese Klasse ar-
beitet auf Strings. String- und Char[]-Objekte machen insgesamt den größten Teil der Speicher-
belegung aus.
56 Kapitel 5 Performance-Analyse
Die Objekte der Domainvalue-Schicht sind wertartig. Für detailliertere Informationen zu Wert-
Semantik wird auf MacLennan [Mac82] und Züllighoven [Z+98, S. 57 ff.] verwiesen. Wertartige
Objekte sind unveränderlich und können deshalb frei wiederverwendet werden (siehe [BGWA08]).
Daher müssen von und in dieser Schicht nur verhältnismäßig wenig Objekte erzeugt werden.
In der Verteilung der Speicherumsätze auf Klassenebene, fallen java.lang.StringBuilder, die
Spring-Klasse BeanFactoryUtils (siehe 2.4.6) und die Hibernate-Klasse Criteria heraus. Sie
verursachen mit 20, 17 bzw. 12 Prozent des insgesamt benötigten Speichers den größten Speicher-
umsatz. Die BeanFactoryUtils werden zum großen Teil aus der Service-Schicht aufgerufen. Ins-
besondere die Methode ServiceCreatorImpl.getService ruft diese auf. In Abschnitt 6.1.3 wird
eine Optimierung diesbezüglich durchgeführt und die Ergebnisse dargestellt.
Die Criteria-Klasse wird fast ausschließlich aus dem service.hibernate-Package verwendet.
Ein kleiner Anteil der Zugriffe erfolgt aus dem Migration-Package. Da diese Klasse des OR-
Mappers die Objekte zu den Daten der Datenbank erstellt, sind große Speicherumsätze zu erwar-
ten. Auffällig ist, dass der FileServiceHibernate alleine für 64 Prozent des Speicherumsatzes von
Criteria verantwortlich ist. Hier bietet sich Optimierungspotential (siehe 6.1.4).
Die weiteren Klassen mit signifikanten Speicherumsätzen sind die aus den JSPs generierten Klas-
sen. Diese werden der Präsentationsschicht zugeordnet. Alle zusammen benötigen sie mit 261 MB
ca. 21 Prozent des gesamten Speicherumsatzes.
Die erste JCommSy-Klasse, die in der Hotspots-Ansicht auftaucht, ist der PHPDeserializer. Die-
ser ist bereits bei der Messung der Ausführungszeit aufgefallen. Vier Prozent des Speicherumsat-
zes des JCommSy fordert die Klasse direkt an. Zusätzlich ist sie für 93 Prozent der Speicheranfor-
derungen des StringBuilders verantwortlich und damit ein weiterer Kandidat für Optimierungen
(siehe 6.1.1).
Zusammenfassend lässt sich feststellen, dass die Programmteile, die für großen Speicherumsatz
sorgen, die Ein- und Ausgaberahmenwerke sind. Servlets und JSPs für die Ausgabe per HTML
und Hibernate und Spring für die Erzeugung und das Einlesen der Objekte. Lediglich die Util-
Schicht fällt aus diesem erwarteten Muster heraus.
5.5.3 Auswirkungen von Ajax
Das JCommSy bietet die Möglichkeit, die Ajax-Technologie13 zu benutzen, um nicht die ganze
Seite bei jeder Anfrage neu zu übertragen, sondern nur die Bereiche, die sich verändert haben. Im
Fall vom JCommSy wird bei einem Wechsel zwischen den Detailseiten zweier Ankündigungen
z. B. der Navigationsbereich links nicht neu übertragen, da er sich nicht ändert. Dies hat für den
Benutzer den Vorteil, dass nicht mehr die ganze Seite neu dargestellt werden muss. Dies steigert
die wahrgenommene Performance (siehe 2.2).
Die Gegenüberstellung der Performance auf dem Server im Fall von aktiviertem und deaktivier-
tem Ajax kann nicht mit demselben Benchmark untersucht werden, wie die restliche Performance.
13Ajax steht für „Asynchronous JavaScript and XML“.
5.5 Performance des JCommSy 57
Die Anfragen müssen spezielle Ajax-Informationen enthalten. Der dafür verwendete Benchmark
wird so definiert, das er 10 mal zwischen zwei Ankündigungsdetailseiten hin und her wechselt.
Er entspricht damit einem kleinen Makro-Benchmark, mit nur einem Use-Case als Grundlage.
Die Ergebnisse, die bei den Messungen herauskommen, lassen demnach nur Rückschlüsse darauf
zu, ob sich in diesem Testfall Performance-Vorteile ausmachen lassen oder nicht. Deshalb dürfen
gesparte Sekunden nicht absolut betrachtet werden oder als allgemeingültig für das JCommSy an-
genommen werden.
Die Antwortzeiten, wie sie mit dem JMeter gemessen werden, liegen fast gleich auf. Die mini-
malen Antwortzeiten sind ohne Ajax kürzer. Die durchschnittlichen Antwortzeiten sind allerdings
mit Ajax kürzer. Mit ca. 3 Prozent Unterschied, liegen die Abweichungen aber jeweils im Bereich
von maximal 20 Millisekunden. Die Messergebnisse können im Anhang in Abbildung A.2 und
A.3 angesehen werden.
Schaltet man Ajax ein, dann reduziert sich die Anzahl der Methodenaufrufe auf dem Server. Für
das tags-Package, welches alleine 33 Prozent der Ausführungszeit für diesen Benchmark aus-
macht, ergeben sich mit 8292 Aufrufen 1520 Aufrufe weniger, als ohne Ajax. Auf dem betrach-
teten System brachte das ein Geschwindigkeitsvorteil von 16 Prozent für dieses Package (13 Se-
kunden). Für service.impl werden 320 Aufrufe weniger registriert, also nur noch 1631. Dies
machte sich in 27 Sekunden kürzerer Ausführungszeit bemerkbar (30 Prozent). Die gesamte Aus-
führungszeit für den Benchmark lag bei 294 Sekunden ohne Ajax und bei 255 Sekunden mit Ajax.
Nicht beachtet wird hierbei, dass im Ajax-Fall nicht für jede Anfrage die Bilder und css-Dateien
neu geladen werden müssen. Dadurch spart man Anfragen und damit Zeit. Speichert ein Browser
diese Dateien lokal zwischen, entfällt auch dieser Vorteil.
Der maximale Speicherverbrauch liegt mit Ajax etwas höher, jedoch mit 29 zu 28 MB im Be-
reich der Schwankungen, die als nicht signifikant betrachtet werden können. Dies kann z. B. daran
liegen, dass das Garbage Collecting später ausgelöst wurde, weil die Anfragen schneller hinter-
einander ankamen.
Insgesamt lässt sich mit aktiviertem Ajax ein Performancevorteil für den Server ausmachen. Im
Verhältnis zu den am JCommSy durchgeführten Optimierungen ist er jedoch klein. Die vom Be-
nutzer wahrgenommene Performance der Anwendung steigt durch Ajax signifikant.
5.5.4 Bewertung
Die Analyse hat drei große Ansatzpunkte identifiziert, die optimiert werden können. Die entspre-
chenden Klassen sind jeweils sowohl bezüglich der Zeit, als auch bezüglich des Speichers heraus-
gefallen. Besonders auffällig ist die unerwartet hohe Bedeutung der Util-Schicht für die Perfor-
mance. Im nächsten Kapitel werden die Optimierungen detailliert vorgestellt und bewertet.
Die absoluten Zeiten, die bei der Messung mit dem Profiler herausgekommen sind, sind stark
durch den Profiler gekennzeichnet und dürfen dementsprechend nur vergleichend betrachtet wer-
den.
58 Kapitel 5 Performance-Analyse
5.6 Zusammenfassung
Eine Performance-Analyse hat das Ziel die Performance eines Systems zu verbessern. Dafür wird
dies untersucht, Änderungen identifiziert, umgesetzt und deren Auswirkungen erfasst. Um das zu
leisten, werden eine abgeschlossene Testumgebung und Software-Werkzeuge benötigt. Mit JMe-
ter lassen sich HTTP-Anfragen generieren und wie ein Benchmark abspielen. Der JProfiler ist ein
Profiling-Werkzeug, das eingesetzt wird, um unterschiedliche Aspekte wie Ausführungszeit und
Speicherverbrauch zu messen. Für die Betrachtung der Performance lassen sich die drei Ebenen
Architektur, Klassen und Methode einnehmen. Auf Architekturebene ist es schwierig zu messen,
deshalb muss entsprechend aggregiert werden.
Die Performance Skala lässt sich in die fünf Kategorien I Java-Konstrukte, II Standardmethoden,
III Schleifen, IV Ein-/Ausgabe und V Algorithmen einteilen. Die einzelnen Bausteine aus denen
eine Anwendung besteht, wie z. B. ein Methodenaufruf oder ein Dateizugriff, können diesen Ka-
tegorien zugeordnet werden.
Das JCommSy verbringt über 70 Prozent seiner Ausführungszeit in den Schichten service und
util. Presentation beansprucht 20 Prozent. Alle drei zusammen sind für über 90 Prozent des
Speicherumsatzes verantwortlich. Die anderen Schichten sind, aus dem Performance-Blickwinkel
betrachtet, uninteressant. Die Klassen PHPDeserializer, IncludeTag und FileServiceHibernate
verbrauchen signifikant Ausführungszeit und sind Ansatzpunkte für Optimierungen.
59
Kapitel 6
Ergebnisse der Refactorings undOptimierungen am JCommSy
Die beiden vorhergehenden Kapitel haben Schwachstellen des JCommSy bezüglich der Architek-
tur und der Performance aufgedeckt. In diesem Kapitel werden die daraus abgeleiteten Refacto-
rings und Optimierungen vorgestellt und deren Ergebnisse dargestellt. Abschließend werden die
Auswirkungen aller Optimierungen insgesamt erörtert und bewertet.
6.1 Durchgeführte Refactorings und Optimierungen
Im Folgenden werden die Refactorings und Optimierungen, die im Rahmen dieser Arbeit an dem
JCommSy durchgeführt wurden, vorgestellt und ihre Auswirkungen dargelegt. Die Optimierungen
„Deserialisierung“, „Spring Dependency Injection“ und „FileServiceHibernate erweitern“ werden
motiviert, deren Durchführung und die Resultate vorgestellt. Das Refactoring „Fachlogik extra-
hieren“ optimiert die Architektur des JCommSy. Dessen Auswirkungen auf die Performance und
die Architektur werden untersucht und die Ergebnisse dargestellt.
Die Auswahl der Optimierungen ist nach dem unter 2.2.5 beschriebenen Verfahren erfolgt. Es
wurde also jeweils zuerst die Optimierung durchgeführt, die am einfachsten schien. Die Reihen-
folge innerhalb dieses Kapitels entspricht der Reihenfolge in der die Änderungen am JCommSy
durchgeführt wurden. Da vor und nach jeder Optimierung gemessen wurde, beziehen die in den je-
weiligen Abschnitten angegebenen Messergebnisse die zuvor durchgeführten Optimierungen mit
ein. Angegebene absolute Zahlen, wie Ausführungszeiten oder Anzahl an Aufrufen, beziehen sich,
wenn nicht explizit anders angegeben, auf den für diese Arbeit entwickelten Benchmark (siehe
5.2.3).
6.1.1 Deserialisierung
Das Profiling zeigt, dass die util.PHPDeserializer-Klasse 35 Prozent der Zeit verbraucht. Sie ist
einer der ersten Kandidaten für Optimierung. Die Klassen PHPDeserializer und PHPSerializer
lagen ursprünglich im migration-Package. Ihre Namen legen nahe, dass sie PHP spezifisch sind.
Dies ist jedoch nur bedingt der Fall. Das PCommSy hat das Datenbankschema des JCommSy vor-
gegeben. Das Schema hat sich mit der Zeit weiterentwickelt, ist also gewachsen. Es sieht vor, dass
unterschiedliche Informationen von Items gemeinsam in einem String im Extras-Feld abgelegt
werden. Wird z. B. an einer Gruppe ein Bild gespeichert, so landet der Verweis auf dieses Bild im
Extras-Feld. Da unterschiedliche Informationen in dieses Feld gespeichert werden, wurde für die
60 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
Speicherung innerhalb dieses Felds zunächst die Extensible Markup Language (XML) vorgese-
hen. Im Zuge der Weiterentwicklung wurde XML aufgegeben und stattdessen eine andere Form
der Speicherung gewählt, die bereits zum Serialisieren der Sitzungsinformationen benutzt wird.
Die beiden Klassen PHPSerializer und PHPDeserializer sind dafür verantwortlich, eine Objekt-
struktur in diese Form zu serialisieren, bzw. aus dem serialisierten String wieder eine entspre-
chende Objektstruktur aufzubauen. Sie sind also aufgrund des aufgeweichten Datenbankschemas
nötig. Solange es ein Extras-Feld gibt, werden die beiden Klassen benötigt.
Ist-ZustandDas Profiling ergibt, dass die Methoden PHPDeserializer.getToken und PHPDeserializer.check
24 bzw. 5 Prozent der Gesamtlaufzeit ausmachen. Beide sind mit 59 bzw. 2 Mikrosekunden (Ka-
tegorie II) nicht langsam verglichen mit anderen Methoden, sie werden jedoch vergleichsweise
häufig aufgerufen. Mit 1,3 Millionen bzw. 6 Millionen Aufrufen werden sie Faktor 10 bis 100
mal so oft aufgerufen, wie vergleichbare Methoden von Items (< 100 000 Aufrufe). Man kann im
JProfiler erkennen, dass getToken rekursiv wiederum von getToken aufgerufen wird. Die Rekur-
sion hat ihren Anfang in der Methode PHPDeserializer.deserialize, von wo aus sie gestartet
wird. Diese Methode wird wiederum von ExtrasItemImpl.getExtra eine Millionen mal aufgeru-
fen. ExtrasItemImpl ist eine Oberklasse vieler Items und bietet Zugriff auf die Informationen des
Extras-Felds.
Die Klasse ExtrasItemImpl ist so realisiert, dass sie sich intern den String, wie er im Extras-Feld
der Datenbank steht, in einer Exemplarvariable hält. Bei jeder Anfrage, die Informationen aus
dem Extras-Feld liefern soll, wird das Extras-Feld vom PHPDeserializer deserialisiert, so das ei-
ne Objektstruktur wie z. B. eine Map entsteht. Diese wird dann verwendet, um die gewünschten
Informationen zurückzugeben. Soll eine Veränderung am Extras-Feld gemacht werden, z. B. ei-
ne zusätzliche Information gespeichert werden, dann wird erst deserialisiert, die Veränderung am
Objekt gemacht und dieses danach wieder serialisiert. Diese Umsetzung ist konzeptionell einfach.
Darüber hinaus ermöglicht sie, dass Subklassen kein Wissen darüber benötigen, wie die Informa-
tionen in der Datenbank gespeichert werden.
VorgehenAls erster Schritt beim Optimieren muss ein Ansatzpunkt ausgewählt werden. Da die Methoden
von PHPDeserializer bereits kurze Ausführungszeiten hatten, wurde der Ansatz gewählt, die An-
zahl der Aufrufe zu verringern. Dafür wird ExtrasItemImpl genauer betrachtet.
Die erste Idee für eine Optimierung ist, die ExtrasItemImpl-Klasse so umzustellen, dass nur noch
genau dann serialisiert wird, wenn Hibernate mittels getExtras den String ausliest, der in die Da-
tenbank geschrieben wird. Analog wird nur noch genau dann deserialisiert, wenn Hibernate den
String mittels setExtras setzt. Dies hätte zur Folge, dass unabhängig von der Anzahl der Zugriffe
auf die Extras eines Items, immer genau einmal serialisiert und einmal deserialisiert wird.
ResultatDie Umstellung, dass ExtrasItemImpl immer beim Setzen und Abfragen zu (de)serialisieren, sorg-
te dafür, dass getToken nur noch 226 000 mal aufgerufen wurde und damit nur noch ein Prozent
6.1 Durchgeführte Refactorings und Optimierungen 61
der Gesamtzeit des JCommSy brauchte. Trotzdem kam es zu einer Verschlechterung der Perfor-
mance. Das Setzen und Abfragen wird von Hibernate immer gemacht, auch dann, wenn gar nicht
(de)serialisiert hätte werden müssen, weil auf das Feld gar nicht zugegriffen wurde und/oder es
nicht verändert wurde. Verstärkt wird dies dadurch, dass Hibernate immer gleich mehrere Daten-
sätze auf einmal von der Datenbank abfragt und die Objekte erzeugt.
Als Folge dieses Optimierungsversuchs wird PHPSerializer zu einem Hotspot, der 53 Prozent
der Laufzeit verbraucht. Dessen unterschiedliche Serialisierungs-Methoden verbrauchen die Zeit
und führen zu einer Verschlechterung der Performance. Das legt nahe, dass die Klasse unterschei-
den können muss, zwischen Hibernate Zugriffen und der eigentlichen Benutzung des Felds. Des
Weiteren verdeutlicht dieses Szenario, wie wichtig es ist, jede vermeintliche Optimierung durch
Messungen zu bestätigen.
Vorgehen (2. Versuch)Hibernate benutzt die als privat deklarierten Methoden getExtras und setExtras. Diese Metho-
den tun nichts, außer den String aus der Exemplarvariable zu lesen, bzw. in diese zu schreiben.
Über die öffentlichen Methoden getExtra und setExtra können Informationen unter einem über-
gebenen Schlüssel im Extras-Feld abgelegt werden. Diese Methoden sorgen dafür, dass erst beim
ersten lesenden Zugriff deserialisiert wird und nur dann serialisiert wird, wenn ein schreibender
Zugriff stattgefunden hatte und Hibernate mittels getExtras den String für die Datenbank anfor-
dert. Die eigentliche Rekursion wurde nicht beschleunigt, sondern nur die Anzahl an Aufrufen
dieser Rekursion verringert.
ResultatDie Optimierung reduziert die Aufrufe an PHPDeserializer, so dass dieser nur noch 4 Prozent der
Laufzeit benötigt. Der Preis für die deutliche Verbesserung der Performance ist die Komplexität
der Klasse. Es wurden drei neue Exemplarvariablen eingeführt, in denen sich die Klasse merkt,
ob (De)Serialisierung benötigt wird. Die Methoden sind komplexer und damit fehleranfälliger
und schwieriger zu lesen. Die zusätzlichen Exemplarvariablen werden von allen Items geerbt. Der
Verdacht, dass dadurch der Speicherverbrauch der Anwendung steigt, lässt sich jedoch nicht be-
stätigen, sondern wird durch Messungen widerlegt. Es werden so signifikant viel weniger Objekte
erzeugt, dass die zusätzlichen Felder nicht ins Gewicht fallen. Die Util- und Item-Schichten for-
dern 81 bzw. 84 Prozent weniger Speicher an. Die 89 Prozent Geschwindigkeitsgewinn innerhalb
der Util-Schicht wurden also nicht gegen zusätzlichen Speicherverbrauch getauscht, sondern re-
duzierten diesen gleichzeitig.
Das Extras-Feld in der Datenbank sollte entfernt werden und entsprechende einzelne Felder, für
die enthaltenen Daten, angelegt werden. Dies ist jedoch nur in Absprache mit dem PCommSy
möglich, bzw. wenn das JCommSy alleine voll funktionsfähig ist.
6.1.2 Fachlogik extrahieren
Die Architektur des JCommSy sieht vor, dass die Präsentationsschicht gekapselt ist. Die Fachlogik
des JCommSy soll unabhängig von der Darstellung sein. Diese Fachlogik umfasst Funktionalitä-
62 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
ten wie das Erzeugen neuer Ankündigungen, das Verknüpfen von Ankündigungen mit Materialien
oder das Editieren von Gruppen. Vor dem Refactoring war die Fachlogik zu großen Teilen in den
Ausstattern (siehe auch 4.4). Im Rahmen der Diplomarbeiten von Thiesen und Daricili ([Thi09]
und [Dar09]) wird die JSP-Oberfläche durch alternative Oberflächen ersetzt. Diese Oberflächen
verwenden die Fragmente und Ausstatter nicht. Da die Fachlogik jedoch in den Ausstattern ent-
halten ist, mussten sie vor diesem Refactoring zwingend verwendet werden, um die Fachlogik
wiederverwenden zu können. Durch das Refactoring wird die Fachlogik von den Ausstattern ent-
koppelt und damit wiederverwendbar. Die Fachlogik wird in eine eigene Schicht extrahiert.
...
forward
forward
TomcatMigrationFilter
CommsyServlet
FragmentServlet jsp Outfitter
checkUser/Session
request
response
isSupportedModuledoFilter
updateFragment
createParamBean
buildOutfitterTree(paramBean)
buildFragmentTree(paramBean)
doGet
updateItem ...
Abbildung 6.1: Sequenzdiagramm einer Anfrage vor dem Refactoring (Ausschnitt).Durch . . . ist der Zugriff auf die unteren Schichten angedeutet.Die Farben ordnen die Elemente den Schichten aus Abbildung 4.2 zu.
Ist-SituationIn Abbildung 6.1 ist der Ablauf einer Anfrage an das JCommSy in Form eines UML Sequenzdia-
gramms1 dargestellt. Zunächst entscheidet der MigrationFilter, ob die Anfrage vom JCommSy
oder vom PCommSy behandelt werden soll. Das daraufhin aufgerufene CommsyServlet überprüft,
ob ein Benutzer angemeldet ist und lädt die entsprechende Sitzung. Das anschließend aufgerufene
FragmentServlet hat die Aufgabe, den Bau des Ausstatter- und Fragmentbaums anzustoßen, wel-
1siehe [Sv06, S. 79 ff.]
6.1 Durchgeführte Refactorings und Optimierungen 63
response
request
...
doFilter
forward
...
forward
forward
Tomcat MigrationFilter
CommsyServlet
FragmentServlet
jsp Outfitter
checkUser/Session
isSupportedModule
updateFragment
createParamBean
buildOutfitterTree(paramBean)
buildFragmentTree(paramBean)
doGet
updateItem
LogicServlet
Com-mand
...
execute
Abbildung 6.2: Sequenzdiagramm einer Anfrage nach dem Refactoring (Ausschnitt).Durch . . . ist der Zugriff auf die unteren Schichten angedeutet.Die Farben ordnen die Elemente den Schichten aus Abbildung 4.2 zu.
che die Daten für die Anzeige zusammenstellen und vorbereiten. Abschließend wird die Anfrage
an die JSPs weitergeleitet, in denen unter Verwendung der Fragmente die HTML-Seite generiert
wird.
In Abbildung 6.1 ist die Fachlogik exemplarisch durch die Methode updateItem dargestellt, wel-
che von den Ausstattern ausgeführt wird. Die Zugriffe auf die unteren Schichten sind in dem
Diagramm weggelassen, da sie sich durch das Refactoring nicht geändert haben.
VorgehenDie ursprüngliche Aufgabe der Ausstatter war das Befüllen der Fragmente mit den anzuzeigen-
den Daten. Im Laufe der Entwicklung des JCommSy haben die Ausstatter jedoch darüber hin-
ausgehende Aufgaben übernommen, wie beispielsweise das Speichern von Einträgen. Sie haben
damit einerseits die Aufgabe, Daten in das System hinein zu leiten und andererseits diese für die
Ausgabe aufzubereiten. Diese zwei grundsätzlich unterschiedlichen und voneinander unabhängi-
gen Funktionalitäten werden getrennt. Vor das FragmentServlet wird ein weiteres Servlet, das
LogicServlet, geschaltet (siehe Abbildung 6.2).
Das LogicServlet ist von Ausstattern und Fragmenten unabhängig. Es analysiert die eingehen-
den Daten und ruft das entsprechende Command auf. Ein Command stellt eine fachliche Aktivität dar
64 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
Request Session
enthält Daten aus
ParamBean
liest
Command
verändert
Item
benutzt
Services
liest
Outfitter
benutzt
füllt
Fragment
liest
jsp
Abbildung 6.3: Schematischer Ablauf einer Anfrage an das JCommSyDie Farben ordnen die Elemente den Schichten aus Abbildung 4.2 zu.
und kapselt diese. Hat der Benutzer z. B. auf Bearbeiten einer Ankündigung geklickt, ist diese
Information in der am Server ankommenden HTTP-Anfrage enthalten. Die ParamBean macht die
Daten der Anfrage typisiert verfügbar. Aus dieser ParamBean extrahiert das LogicServlet die In-
formation, um das zuständige Command zu ermitteln. Dieses wird mittels Spring und Dependency
Injection erzeugt (siehe 2.4.6). Anschließend wird das Command ausgeführt und bearbeitet das
Item. Nachdem das Command ausgeführt wurde, kommt der Kontrollfluss zum FragmentServlet,
von wo die Ausstatter und Fragmente ausgeführt werden. Dieser Ablauf ist in Abbildung 6.2 als
Sequenzdiagramm dargestellt. Abbildung 6.3 stellt den Informationsfluss innerhalb des JCommSy
schematisch dar.
Die Bearbeiten-Seiten verhalten sich etwas anders, da die eingegebenen Informationen erhalten
bleiben müssen, ohne dass sie in die Items geschrieben werden. Am Beispiel einer Bearbeiten-
Seite einer Ankündigung wird dies verdeutlicht. Ein AnnouncementEditItemCommand wird erzeugt
und ausgeführt. Als Ein- und Ausgabeparameter erhält das Command die ParamBean und die Ser-
vices, wie alle anderen Commands auch. Im Fall eines Bearbeiten-Commands besteht dessen Auf-
gabe darin, aus der ParamBean die ID der Ankündigung zu lesen. Über die Services wird aus der
Datenbank das zugehörige AnnouncementItem geladen. Die Daten werden daraus gelesen und in
eine ItemEditBean geschrieben. Diese ItemEditBean wird als Ergebnis dieses Commands an die
ParamBean angefügt. Das LogicServlet leitet die Anfrage an das FragmentServlet weiter, wo der
zugehörige Ausstatter die ItemEditBean aus der ParamBean lädt und deren Informationen darstellt.
Dies ist schematisch und vereinfacht in Abbildung 6.4 zu sehen.
6.1 Durchgeführte Refactorings und Optimierungen 65
Request Session
enthält Daten aus
ParamBean
liest
Command
benutzt
Item
benutzt
Services
fülltItemEditBean
liestEditOutfitter
füllt
Fragment
liest
Fragment
jsp
Abbildung 6.4: Schematischer Ablauf einer Anfrage für eine BearbeitenseiteDie Farben ordnen die Elemente den Schichten aus Abbildung 4.2 zu.
Ein Fragment und eine ItemEditBean sind beides Datenbehälter, sie haben jedoch unterschiedli-
che Aufgaben. Ein Fragment enthält genau die Informationen, die in einer JSP angezeigt werden
sollen. Fragmente werden bei jeder Anfrage neu befüllt, haben also eine Gültigkeitsdauer von ei-
ner Anfrage.
Im Gegensatz dazu, ist eine ItemEditBean über mehrere Anfragen hinweg gültig, solange bis
das Editieren abgebrochen oder gespeichert wird. Sie enthält die Informationen, die mit diesem
Editiervorgang verbunden sind. Beispielsweise wird die Beschreibung einer Ankündigung in der
ItemEditBean zwischengespeichert, bis der Benutzer das Speichern veranlasst. Die Informationen
in der ItemEditBean sind nicht auf eine JSP zugeschnitten, sondern auf den Vorgang „Editieren“.
Sie können von mehr als einem Ausstatter benutzt werden und so in mehr als ein Fragment (und
damit JSP) gelangen.
Zusammenfassend lassen sich damit folgende drei Arten von Beans im JCommSy ausmachen, die
jeweils ihren eigenen Aufgabenbereich haben:
Sitzung Die ParamBean enthält die Daten aus der Anfrage und der Antwort, sowie der Sitzung.
Editiervorgang Die ItemEditBean enthält die Daten eines fachlichen Vorgangs (z. B. Editieren),
wird von Commands befüllt und von einem oder mehreren Ausstattern lesend verwendet.
Darstellung Ein Fragment enthält die Daten für genau eine JSP und wird von genau einem Aus-
statter befüllt.
Vor dem Refactoring erlaubte die Architektur, dass die ItemEditBeans direkt aus den JSPs benutzt
werden. Der Zugriff auf die ItemEditBean ermöglicht das Sparen einer Indirektionsstufe. Die Da-
ten mussten nicht erst von den Ausstattern in die Fragmente geschrieben werden, sondern konnten
direkt aus den ItemEditBeans gelesen werden. Die Ausstatter hatten so nicht die Möglichkeit,
die genaue Form der Darstellung festzulegen, bzw. wenn sie es getan haben, dann haben sie es
66 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
für alle Stellen festgelegt, in denen die Daten benutzt werden. Durch die Aufteilung ist es jetzt
z. B. möglich, das selbe Datumsobjekt an unterschiedlichen Stellen verschieden darzustellen. Die
zusätzliche Indirektionsstufe macht außerdem die Zuständigkeiten klarer deutlich. Die Ausstatter
werden wieder zu reinen Ausstattern und können als solche einfacher verstanden und weiterent-
wickelt werden. Die zusätzliche Indirektionsstufe betrifft die Performance-Kategorie I und wirkt
sich also im Bereich von Nanosekunden aus. Dies wird durch die Ausführung der JSP um drei
Kategorien überwogen.
Die ItemEditBean enthält alle Daten die während eines Editiervorgangs eingegeben werden. Es
ist nötig, diese an der Sitzung zu speichern, weil die CommSy-Benutzungsoberfläche vorsieht,
dass man aus einer Editieransicht auf eine andere Seite gelangt, von der aus man z. B. ande-
re Items dem aktuellen Item zuordnen kann. Der Editiervorgang wird dabei weder abgebrochen
noch abgeschlossen. Hat man seine Auswahl getätigt, kommt man wieder zurück zur Editieran-
sicht. Hier müssen die zuvor eingegebenen Werte zur Verfügung stehen. Sie werden dafür aus der
ItemEditBean gelesen. Sobald eine Editiersitzung durch Speichern, Abbrechen oder Löschen be-
endet wird, wird die ItemEditBean gelöscht.
Die Auswirkungen der zusätzlichen Schicht, sowie der zusätzlichen Indirektionsstufen und der
Verwendung des Command-Musters auf die Performance des JCommSy, werden im Folgenden
untersucht.
ResultatDas Refactoring hat folgende konzeptionelle, die Architektur betreffenden, Auswirkungen:
• Die Ausstatter sind übersichtlicher geworden. Die Ausstatter für Detail- und Indexseiten
arbeiten weiterhin nur auf den Items und befüllen mit deren Daten die Fragmente. Aus-
statter für Editierseiten arbeiten nur auf den ItemEditBeans und füllen mit deren Daten die
Fragmente. Zuvor mussten sie sowohl mit ItemEditBeans als auch mit Items umgehen.
• Die Ausstatter sind nur noch dafür da, die Fragmente zu befüllen. Sie haben nicht mehr
die Aufgabe die Items und die ItemEditBeans zu aktualisieren. Konzeptionell brauchen die
Ausstatter für Editierseiten die ParamBean nicht mehr, sondern nur noch die ItemEditBean.
Derzeit sieht die Schnittstelle aller Ausstatter aber die ParamBean als Parameter vor, so dass
dies nicht geändert wird.
• Die verändernde Logik ist komplett von der Darstellung entkoppelt. Das FragmentServlet,
welches die Ausstatter und Fragmente anstößt, ist dem LogicServlet nachgeschaltet und
das LogicServlet ist komplett unabhängig vom nachfolgenden FragmentServlet. Alterna-
tive Oberflächen können auf die Benutzung des FragmentServlets verzichten und haben
trotzdem die gesamte Logik zugreifbar. Dies war vorher nicht möglich, da die Logik in den
Ausstattern enthalten war.
• Die Fachlogik ist von der Oberflächentechnologie entkoppelt. Die Commands werden zwar
von dem LogicServlet ausgewählt und ausgeführt, haben jedoch selber keine Abhängig-
keiten von Komponenten des Servlet-Rahmenwerks. Sie benötigen lediglich die ParamBean
6.1 Durchgeführte Refactorings und Optimierungen 67
und die Services. Auch der Auswahlmechanismus innerhalb des LogicServlets ist durch
die ParamBean von der Anfrage, und somit vom Servlet-Rahmenwerk, entkoppelt.
• Die Commands werden, wie die Services, mittels Spring und Dependency Injection erzeugt.
Gäbe es Test-Services, also Implementierungen der Services speziell für Tests, könnte diese
einfach für die Tests der Logik-Klassen benutzt werden. Solche Test-Services sind derzeit
nicht vorhanden, so dass relativ aufwendig Mock-Objekte für die Services erstellt werden
müssen. Dies macht den Quelltext der Logik-Tests lang und schwierig lesbar.
• Die Abhängigkeiten der JSPs von den ItemEditBeans sind gelöst. JSPs arbeiten ausschließ-
lich auf den Fragmenten.
Das Refactoring hat eine neue Schicht in die JCommSy-Architektur eingeführt. Es wurden neue
Klassen erstellt und diese zur Laufzeit ausgeführt. Der Umbau hat das nach außen sichtbare Ver-
halten nicht geändert und entspricht somit der Definition eines Refactorings [Fow99, S. 53]. Trotz-
dem könnten sich Auswirkungen auf die Performance ergeben. Die Messungen zeigen jedoch, dass
die Änderungen an der Gesamtperformance der Anwendung deutlich kleiner als die Schwankun-
gen der Messungen sind. Das Hinzufügen einer Schicht, die zusätzliche Benutzung des Command-
Musters (nach [GHJV95]), die Einführung mehrerer neuer Klassen und eines Servlets hat keine
Auswirkungen auf die Gesamtperformance.
Die zusätzliche Schicht und das Entwurfsmuster sind in Kategorie I einzuordnen. Die neuen Klas-
sen und die Servlets in Kategorie II. Diese sind, gegenüber den Kategorie IV Aspekten JSP und
Datenbankzugriff vernachlässigbar. Die Auswirkungen sind so klein, dass sie in den Schwankun-
gen innerhalb der Kategorie IV verschwinden.
Ein weiterer Punkt der verhindert, dass die Performance sich verschlechtert ist, dass der Ausstat-
terbaum seltener aufgebaut und ausgeführt wird. Dies passiert nur noch, wenn eine Seite ausge-
liefert werden soll. Gab es vorher eine Weiterleitung innerhalb des JCommSy, so mussten zuerst
die Ausstatter ausgeführt werden, bevor dies festgestellt werden konnte. Durch die sauberere Tren-
nung von Logik- und Darstellungsaspekten (also durch eine striktere Architektur) konnte unnötige
Berechnungen der Kategorie II und III gespart werden.
Die Logik-Schicht macht mit 0,57 Prozent einen vernachlässigbaren Anteil der Ausführungszeit
aus (siehe Tabelle 6.1 in Abschnitt 6.3.1). Die 0,57 Prozent werden durch die aus den Ausstattern
in die Logik-Schicht verschobenen Funktionalitäten ausgemacht.
6.1.3 Spring Dependency Injection
Das Profiling nach den zwei in den vorherigen Abschnitten durchgeführten Umbaumaßnahmen
ergibt, dass die Methode BeanFactoryUtils.beanOfType mit 37 Prozent einen großen Teil der
Laufzeit verursacht, wobei diese durch nur 5510 Aufrufe verbraucht wird. Die Klasse gehört nicht
zum JCommSy-Quelltext, sondern zum Spring-Rahmenwerk (siehe 2.4.6). In einer XML-Datei
spezifiziert das JCommSy, welche Klassen unter welchem Namen von Spring verwaltet werden.
68 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
Spring ist dafür verantwortlich, ein Objekt der Klasse zu erzeugen und alle Abhängigkeiten die-
ser Klasse zu erfüllen. Die Klasse BeanFactoryUtils ist eine Hilfsklasse, die es ermöglicht ein
Objekt zu erhalten, dass einem bestimmten Typ genügt. Beispielsweise ist in der XML-Datei kon-
figuriert, dass es eine Klasse AnnouncementServiceHibernate gibt. Diese Klasse implementiert
das AnnouncementService-Interface. Benötigt ein Ausstatter einen AnnouncementService, kann er
einen solchen erhalten, ohne dass er dafür die Hibernate-spezifische Implementierungsklasse ken-
nen muss. Diese bleibt komplett im impl-Package der Service-Schicht gekapselt.
Im JCommSy wird dieser Spring Mechanismus nicht nur für die Services verwendet, sondern
auch zum Erzeugen der Ausstatter und Commands. Zusätzlich bietet er einen Zugriffspunkt auf
die CommsyProperties. Dieses Objekt enthält JCommSy-spezifische Einstellungen, wie z. B. die
URL2 unter der das JCommSy verfügbar ist.
Ist-Situation
Von den Aufrufen von beanOfType stammen 93 Prozent vom ServiceCreator, 6 Prozent vom
OutfitterCreator und weniger als ein Prozent vom CommandCreator. Wiederum über 85 Prozent
der Zugriffe auf den ServiceCreator passieren aus den Tag-Klassen, um die CommsyProperties
zu erhalten. Diese Funktionalität wird von den Tags benutzt um Links mit der richtigen URL zu-
sammen bauen zu können. Ungefähr 30 Prozent der gesamten Laufzeit wird dadurch verursacht,
dass der LinkTag die CommsyProperties abfragt.
Die Anzahl an Aufrufen von beanOfType ist mit 5510 nicht besonders groß, die Ausführungszeit
mit durchschnittlich 122 ms ist aber lang (Kategorie IV). Ähnlich lange durchschnittliche Aus-
führungszeiten sieht man bei Criteria.list, wo es aber unvermeidlich ist, da die Daten in der
Datenbank liegen und ein Zugriff darauf nötig ist.
Die Architektur des JCommSy sieht vor, dass ein ContextListener beim Start der Anwendung
Spring initialisiert. Im gleichen Schritt wird unter anderem die Datei commsy.properties gelesen
und ein CommsyProperties-Objekt erstellt. Dieses wird unter dem Namen „commsyProperties“
als Attribut am ServletContext gesetzt. Über den ServletContext kann auf die Einstellungen
von anderen Stellen aus zugegriffen werden.
Entgegen diesem Prinzip wird derzeit in jedem CommSyTag ein neuer ServiceCreator erzeugt,
dieser nach den CommsyProperties gefragt und diese anschließend benutzt. Das Erzeugen des
ServiceCreators erfordert das Einlesen der Konfigurationsdateien, deren Überprüfung auf Gül-
tigkeit und weitere Schritte. Mehrere Tags erweitern das CommSyTag, so dass es zu mehr als 3500
Aufrufen kommt.
Vorgehen
Die Tags wurden so angepasst, dass sie die CommsyProperties aus dem ServletContext benutzen.
Die Funktionalität wurde in eine gemeinsame Oberklasse extrahiert.
2Ein Uniform Resource Locator (URL) identifiziert und lokalisiert eine Ressource in einem Netzwerk.
6.1 Durchgeführte Refactorings und Optimierungen 69
ResultatDurch die Benutzung des existierenden CommsyProperties-Objekts werden 3708 Aufrufe von
BeanFactoryUtils.beanOfType gespart. Dies bringt einen Geschwindigkeitszuwachs von 84 Pro-
zent für diese Methode. Damit reduziert sich die Gesamtzeit, die die Anwendung in dieser Metho-
de verbringt, von 37 Prozent auf 9 Prozent, die Anwendung hat sich um 28 Prozent beschleunigt.
Weiterhin muss es einmal einen Zugriff auf das Dateisystem geben und die Einstellungsdatei gela-
den werden. Für diesen einen Zugriff, wird weiterhin ein Kategorie IV-Aspekt vorhanden bleiben.
Für alle folgenden Zugriffe konnte jedoch durch die Optimierung der Aufwand auf Kategorie II
reduziert werden.
Der IncludeTag wurde ebenfalls so geändert, dass er das bereits existierende CommsyProperties-
Objekt verwendet. Vor den Umbaumaßnahmen war der IncludeTag indirekt für 266 MB Speicher-
Allokationen verantwortlich. Dies entspricht 35 Prozent des Gesamtspeicherumsatzes und liegt
vor dem des FileServiceHibernate mit 100 MB (siehe 6.1.4). Nach der Umbaumaßnahme sind
es nur noch 5 MB.
Diese Optimierung wäre nicht nötig gewesen, wenn klarer kommuniziert worden wäre, dass die
Architektur vorsieht, dass das vorhandene CommsyProperties-Objekt zu verwenden ist. Wäre die
Architektur so klar strukturiert, dass z. B. eine Regel genau vorgibt, wo die Zuständigkeit für
„Einstellungen laden“ liegen, wäre das Problem nicht aufgetreten. Alternativ könnte es eine Ver-
botsregel geben, die beschreibt, dass Klassen aus dem Tags-Package nicht auf Services zugreifen
dürfen.
Eine überprüfbare Regel gibt es nicht, dennoch hätte dieses Problem auch dadurch verhindert
werden können, dass die Vorstellungen über die Zuständigkeit (also die Architektur) besser kom-
muniziert worden wären. An dieser Situation sorgt eine sauberere und ausreichend kommunizierte
Architektur für bessere Performance. Am JCommSy ist gerade das Kommunizieren der Architek-
tur schwierig in ausreichender Güte umzusetzen. Das JCommSy wird durch Lehre-Projekte und
Diplomarbeiten weiterentwickelt. Daraus resultiert, dass halbjährlich das gesamte Entwicklerteam
wechselt und nur wenige Personen das Projekt die gesamte Zeit begleiten. Zusätzlich ist die Ar-
chitektur so komplex, dass eine umfassende Kommunikation und Einarbeitung in der gegebenen
Zeit nicht machbar ist. Deshalb sollte die Architektur möglichst gut durch Regeln überprüfbar
sein. Mit dem Sotographen ist es z. B. möglich festzustellen, dass ein Zugriff aus den Tags auf
die Services passiert. Mit einer Regel die dies beschreibt, könnte dies automatisiert überprüft wer-
den. Dafür müsste wiederum eine regelmäßige Überprüfung stattfinden und vorzugsweise in den
Entwicklungsprozess automatisiert integriert sein.
Neue Ist-SituationDie Performance hat sich deutlich verbessert, jedoch steckt weiterhin Optimierungspotential in der
Anzahl der Aufrufe von beanOfType. Der ServiceCreator generiert 1154 Aufrufe von beanOfType,
die sich alle auf Services beziehen. Diese Services sind zustandslos und die Objekte werden wie-
derverwendet, d. h. Spring liefert für zwei Anfragen nach dem AnnouncementService dieselbe Re-
ferenz zurück. Dennoch ist der Spring-Mechanismus langsam.
70 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
VorgehenDer ServiceCreator wird so umgestellt, dass er in einer Map die Services zwischenspeichert,
die er bereits herausgegeben hat. Wird ein Service erneut angefragt, kann er ohne Spring aus
der Map geliefert werden. Voraussetzung dafür ist, dass die Aufrufe am selben ServiceCreator
passieren. Dafür wird der ContextListener so erweitert, dass er nicht bei jedem Aufruf einen
neuen ServiceCreator erzeugt, sondern für jeden Kontext nur genau einen.
ResultatDie Optimierung bringt einen weiteren Geschwindigkeitsvorteil von 5 Prozent, indem sie die An-
zahl der Aufrufe an beanOfType auf 648 reduziert. Durch diese Optimierung wird die Komplexität
erhöht. Falls die Services zukünftig einen Zustand haben sollten oder zur Laufzeit aus einem an-
deren Grund nicht mehr dasselbe Objekt wiederverwendet werden soll, stellt die Optimierung ein
zusätzliches Hindernis dar. Die Optimierung ist einfach wieder zu entfernen, aber es müsste manu-
ell gemacht werden und ist somit eine zusätzliche Fehlerquelle. Ohne diese Optimierung müsste
die Änderung nur in der entsprechenden XML-Datei von Spring gemacht werden. Die bessere
Performance erhöht hier also die Komplexität der Architektur, indem sie die zusätzliche Regel im-
pliziert, dass Services wiederverwendet werden können und es nicht mehr als ein Exemplar geben
können muss. Im Gegensatz zu der Optimierung bezüglich der Serialisierung (siehe 6.1.1) sind
hier mehrere Klassen und eine XML-Datei betroffen und sie bezieht sich auf zwei Schichten.
6.1.4 FileServiceHibernate erweitern
Das JCommSy verbringt 24 Prozent der Zeit in der Methode FileServiceHibernate.getFiles.
Deren Ausführungszeiten liegen mit durchschnittlich 37 ms in derselben Größenordnung wie an-
dere Service-Methoden, die auf die Datenbank zugreifen, jedoch wird diese Methode mit 1464
Aufrufen ca. 50 mal so oft aufgerufen. JProfiler bietet die Möglichkeit, sich Datenbankzugriffe
anzeigen zu lassen. Durch diese Methode werden 33 Prozent der Datenbankzeiten ausgemacht.
Die Methode bietet das größte Optimierungspotenzial.
Ist-SituationDer Aufrufstack, der vom JProfiler dargestellt wird, gibt Aufschluss darüber, dass die Klasse
AbstractIndexListOutfitter für die vielen Aufrufe verantwortlich ist. Dieser Ausstatter füllt
eine Liste von Zeilen für eine Index-Seite. Das passiert bei jedem Aufruf einer Index-Seite. Jede
Zeile repräsentiert ein Item. Für jedes Item soll die Information dargestellt werden, ob Dateien an
das Item angefügt sind oder nicht.
1 for (Item item : listOfItems) {
2 ...
3 indexlist.addRow(item.getItemID().toString(), ... ,
4 fileService.getFiles(item.getItemID()), ...);
5 ...
6 }
Quelltext 6.1: Benutzung des FileService durch den AbstractIndexListOutfitter
6.1 Durchgeführte Refactorings und Optimierungen 71
Quelltext 6.1 stellt dar, dass der AbstractIndexListOutfitter während er über die Items iteriert,
für jedes Item den FileService nach einem Array von CommSyFiles fragt. Jeder dieser Aufrufe
resultiert in einer Datenbankanfrage.
Die Methode FileService.getFiles wird noch von anderen Klassen benutzt, wie beispielsweise
dem AbstractSaveItemCommand. Diese Klasse ist für das Speichern eines Items zuständig. Sie
behandelt genau ein Item und ruft die Methode dementsprechend selten auf.
VorgehenDer FileService wird um eine Methode erweitert, die es erlaubt, für eine Menge von Items, die
zugehörigen Listen von CommSyFiles zu erhalten. Die Methode hat die Signatur getFilesForIDs(
final Set<ItemID> itemIDs) und liefert ein Objekt vom Typ Map<ItemID, List<CommSyFile>>
zurück. Der Service hat die Möglichkeit, mit nur einer Datenbankanfrage alle CommSyFiles aus
der Datenbank zu laden. Anschließend werden diese in einer Map unter der zugehörigen ItemID
abgelegt.
Der Quelltext des Aufrufers ist in Quelltext 6.2 dargestellt.
1 // get all ids of this index−page2 final Set<ItemID> ids = new HashSet<ItemID>();
3 for (final Item item : listOfItems) {
4 ids.add(item.getItemID());
5 }
67 // get all linked files8 final Map<ItemID, List<CommSyFile>> linkedFiles = fileService.getFilesForIDs(ids);
910 // build rows11 for(Item item : listOfItems) {
12 ...
13 final CommSyFile[] linkedFilesArray = linkedFiles.get(item.getItemID()).toArray(
14 new CommSyFile[0]);
15 indexlist.addRow(item.getItemID(), ... , linkedFilesArray , ...);
16 }
Quelltext 6.2: Ausschnitt AbstractIndexListOutfitter nach der Optimierung
Der Zugriff auf den FileService ist aus der Schleife herausgezogen (Zeile 8). Er passiert genau
einmal und liefert eine Map. In der Schleife wird nur noch auf der Map gearbeitet (Zeile 13-15).
ResultatDie Optimierung sorgt dafür, dass die FileService.getFiles-Methode anstelle von 1464 mal nur
noch 40 mal aufgerufen wird. Die neu hinzugekommene Methode FileService.getFilesForIDs
wird 62 mal aufgerufen, genau einmal pro angezeigter Index-Seite. Das Resultat für das JComm-
Sy ist ein Geschwindigkeitsgewinn von 22 Prozent.
Der Zugriff auf die Datenbank ist unvermeidbar, eine Kategorie IV Performance also inhärent
in der Funktionalität enthalten. Vor der Optimierung war die Performance jedoch durch einen
72 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
Algorithmus geprägt (Kategorie V), der den Zugriff deutlich öfter als nötig ausgeführt hat. Die
Optimierung hat also eine Verbesserung von Kategorie V nach IV bewirkt.
Diese Optimierung hat keine Komplexität zum JCommSy hinzugefügt, weder auf Architektur-
noch auf Klassenebene. Der FileService wurde um eine Methode erweitert, welche keine neue
Funktionalität liefert. Dies ist z. B. vergleichbar mit der addAll-Methode einer Liste. Die Schnitt-
stelle der Liste wird dadurch etwas breiter, teilweise einfacher zu benutzen und es ergeben sich
Vorteile bei der Performance. Dennoch ließe sich dieselbe Funktionalität auch ohne addAll errei-
chen. Gleiches gilt für den FileService.
Die aufrufende Klasse ist 10 Zeilen länger geworden, jedoch konzeptionell nicht schwieriger zu
verstehen. Die Funktionalität der getFiles-Methode lässt sich durch die neue Methode abbilden.
Die ursprüngliche Methode hätte also wegfallen können und alle Aufrufe entsprechend über die
neue Methode passieren können. Dann wäre die Schnittstelle nicht breiter geworden. Dies hätte
jedoch die Klassen, in denen prinzipiell nur die CommSyFiles von einem Item gebraucht werden,
unnötig komplizierter gemacht.
Es stellt sich die Frage, ob die Service-Schicht durch das Hinzufügen dieser Methode implizit von
der Präsentationsschicht abhängig gemacht wurde, bzw. zu sehr auf diese spezielle Oberflächen-
technologie zugeschnitten ist. Dies ist nicht der Fall: Das Benutzungsmodell des CommSy sieht
vor, dass die Übersichtsdarstellung Informationen über mehrere Items gleichzeitig anzeigt. Für
alle diese Items soll die Information, welche Dateien zu ihnen gehören, dargestellt werden. Die
neu hinzugekommene Methode entspricht also einer fachlichen Anforderung und ist nicht auf eine
spezielle Oberfläche zugeschnitten.
6.1.5 StringBuilder benutzen
In Java sind String-Objekte unveränderlich. Die Sprache gestattet Zeichenkettenkonkatenation per
Plus-Operator. Eine Konkatenation zweier Strings führt zu dem Kopieren beider und der Erzeu-
gung eines neuen. Dies sollte, insbesondere in Schleifen, vermieden werden (siehe [BGWA08]).
Die LinkTag-Klasse ist dafür verantwortlich, HTML-Links zusammenzubauen. Dafür werden, ab-
hängig von den Eingabeparametern, viele Strings aneinandergehängt.
VorgehenIm Laufe dieser Optimierung wurden die meisten Konkatenationen innerhalb der Klasse ent-
fernt und durch die Verwendung eines StringBuilders ersetzt. StringBuilder ist ein veränder-
barer Typ, der dafür optimiert ist, eine längere Zeichenkette durch das Aneinanderfügen mehrerer
Strings zu erstellen.
ResultatVor der Optimierung hatte LinkTag insgesamt einen Umsatz von 15 MB (ca. 200.000 Allokatio-
nen). Danach ist der Umsatz um ein Drittel zurückgegangen, so dass er nur noch 10 MB beträgt.
Der Anteil am insgesamt umgesetzten Speicher sinkt von zwei auf ein Prozent.
Die Optimierung hat auf Methodenebene stattgefunden und es sind weder andere Methoden, noch
6.2 Auswirkungen unterschiedlicher JVM-Modi 73
andere Klassen von ihr betroffen. Durch die Verwendung des StringBuilders wurde der Quell-
text länger. Gleichzeitig kommuniziert er das was er tut besser. Die Quelltexte 6.3 und 6.4 zeigen
Ausschnitte, die die Unterschiede verdeutlichen.
1 String url = "";
2 url = url + RequestParameter.CONTEXT_ID.getName() + "=" + getContext();
3 if (_target != null) {
4 url = url + "#" + _target;
5 }
6 ...
7 pageContext.getOut().print(url);
Quelltext 6.3: Ausschnitt LinkTag mit String-Konkatenation
1 final StringBuilder url = new StringBuilder(40);
2 url.append(RequestParameter.CONTEXT_ID.getName()).append("=").append(getContext());
3 if (_target != null) {
4 url.append("#").append(_target);
5 }
6 ...
7 pageContext.getOut().print(url);
Quelltext 6.4: Ausschnitt LinkTag mit StringBuilder
Es ist in Java nicht üblich, dass mehrere Methodenaufrufe am selben Objekt hintereinander ge-
schrieben werden. Die meisten verändernden Methoden haben den Rückgabetyp void. Ein ty-
pisches Beispiel sind Setter-Methoden. Die append-Methode gibt jedoch das veränderte Objekt
zurück, so dass hier diese kompakte Schreibweise gewählt werden kann. Sie hat hier den Vorteil,
dass in einer Zeile url jeweils um einen zusammengehörigen Aspekt erweitert wird (siehe z. B.
Zeile 2 in Quelltext 6.4).
6.2 Auswirkungen unterschiedlicher JVM-Modi
Die Methode IndexList_jsp._jspService und vergleichbare Methoden in aus JSPs generierten
Klassen, sowie die Services, unterliegen bei ihren Ausführungszeiten stärkeren Schwankungen, da
sie besonders viel Ein- und Ausgabe zu leisten haben. Im Laufe der Messungen hat sich für diese
Klassen gezeigt, dass Schwankungen im Bereich von 100 Prozent nach oben oder unten möglich
sind (bei konstanter Anzahl an Aufrufen).
Im Folgenden wird die Performance des JCommSy in unterschiedlichen JVM-Ausführungsmodi
diskutiert. Grundlage für die Messungen ist die Version vom 2. Dezember 2008, die Ergebnisse
sollten jedoch auf andere JCommSy-Versionen übertragbar sein.
Die Java Virtual Machine kann im Client- oder im Server-Modus ausgeführt werden (siehe 2.2.8).
Zusätzlich kann zur Startzeit der JVM festgelegt werden, ob „Assertions“ angeschaltet sind oder
nicht. Dies entscheidet darüber, ob assert-Ausdrücke ausgewertet werden sollen oder nicht. Mit
assert bietet Java die Möglichkeit Bedingungen wie Invarianten in den Quelltext einzubauen, de-
ren Überprüfung zur Startzeit der Anwendung an- oder ausgeschaltet werden kann. Im JCommSy
74 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
wird der assert-Befehl benutzt, um das Vertragsmodell [Mey92] umzusetzen, also insbesondere
Vorbedingungen und Nachbedingungen für Methoden zu formulieren und zu überprüfen.
Es werden folgende vier Ausführungsmodi gegenübergestellt:
Client Dies entspricht den Standardeinstellungen auf dem Testsystem und damit dem Modus, mit
dem alle anderen Profilings in dieser Arbeit durchgeführt wurden. Die JVM wird im Client-
Modus ausgeführt und die Assertions sind ausgeschaltet.
Client/Asserts Hier wurde das JCommSy mit angeschalteter Assertions-Überprüfung im Client-
Modus ausgeführt.
Server Diese Messung wurde mit der JVM im Server-Modus ausgeführt. Die Assertions waren
deaktiviert.
Server/Optimiert Die JVM wurde im Server-Modus gestartet. Zusätzlich wurde der JVM zusätz-
licher Speicher zur Verfügung gestellt und dafür gesorgt, dass die Methoden bereits beim
ersten Aufruf in nativen Code übersetzt werden.
Der Vergleich erhebt nicht den Anspruch allgemeingültig zu sein, er liefert jedoch für das JComm-
Sy folgende Ergebnisse. Es lässt sich feststellen, dass es für die Performance des JCommSy kei-
nen signifikanten Unterschied macht, ob die Assertions ein- oder ausgeschaltet sind. Während die
Schichten domainvalue und util ohne Assertions minimal schneller laufen, ist die Item-Schicht
mit eingeschalteten Assertions minimal schneller. Insgesamt ist das JCommSy mit ausgeschal-
teten Assertions zwei Prozent schneller, der Unterschied ist aber kleiner als die Schwankungen
innerhalb der Service-Schicht, so dass das Ergebnis kaum aussagekräftig ist.
Im Server-Modus ist die Anwendung weniger als 0,2 Prozent schneller als im Client-Modus. Dies
ist wiederum signifikant viel weniger als die Schwankungen der einzelnen Schichten. Für das
JCommSy macht es keinen Performance-Unterschied, ob die JVM im Server- oder im Client-
Modus läuft.
Das Erzwingen des Übersetzens in nativen Code hat das JCommSy um 16 Prozent verlangsamt.
Dies ist also keine Optimierung. Auch der zusätzliche Speicher hat die Performance nicht verbes-
sern können.
Für das JCommSy lässt sich insgesamt feststellen, dass die Performance stark durch Ein- und
Ausgabe geprägt ist (siehe 6.3.1). Die unterschiedlichen Ausführungsmodi der JVM können die
Performance nicht signifikant verändern. Für eine detaillierte Tabelle der Ergebnisse siehe Anhang
A.1.
6.3 Auswirkungen auf Performance und Architektur
Die Ergebnisse jeder einzelnen Optimierung wurden bereits in den zugehörigen Abschnitten dar-
gestellt. Im Folgenden werden die Auswirkungen der Gesamtheit der Änderungen auf das JComm-
Sy betrachtet. Es wird zunächst die Performance in den Fokus gerückt. Für die Ausführungszeit
6.3 Auswirkungen auf Performance und Architektur 75
Schicht Vorher Nachher VerbesserungZeit Anteil Zeit Anteil
service 143.990 ms 35,13 % 39.498 ms 24,63 % 72,57 %util 141.000 ms 34,40 % 18.177 ms 11,34 % 87,11 %presentation 83.682 ms 20,41 % 82.388 ms 51,38 % 1,55 %item 35.798 ms 8,73 % 15.244 ms 9,51 % 57,42 %domainvalue 3.889 ms 0,95 % 2.534 ms 1,58 % 34,84 %migration 1.566 ms 0,38 % 1.587 ms 0,99 % -1,34 %logic 0 ms 0 % 912 ms 0,57 % undefiniertGesamt 409.925 ms 100 % 160.340 ms 100 % 60,89 %
Tabelle 6.1: Vergleich der Ausführungszeiten der Schichten (Stand: 19. Dezember 2008)
wird ein Vergleich zwischen dem nicht-optimierten und dem optimierten JCommSy erstellt. An-
schließend werden die Auswirkungen auf die Architektur zusammengefasst.
6.3.1 Performance
Die Performance des JCommSy hat sich durch die Optimierungen und Refactorings, die in die-
sem Kapitel beschrieben worden sind, verbessert. Tabelle 6.1 stellt gegenüber, wie sich die Aus-
führungszeiten der Schichten verändert haben. Am deutlichsten ist die Verbesserung in der Util-
Schicht mit 87 Prozent. Die Präsentationsschicht hat nur eine kleine Beschleunigung von 1,55
Prozent erfahren. Mehr als 81 der 83 Sekunden dieser Schicht werden durch die JSPs ausgemacht.
Diese haben sich kaum verändert. Das Übersetzen der JSPs in Servlets und wie man die JSPs bzw.
die resultierenden Servlets optimieren könnte, wird hier nicht behandelt.
Die Migrations-Schicht hat ihre Ausführungszeit leicht verschlechtert. Die Änderungen liegen im
Bereich von 20 ms. Die Ursache können Änderungen durch die Weiterentwicklung des JCommSy
sein oder z. B. in unterschiedlich lang dauernden Festplattenzugriffen begründet sein.
Bei der Logik-Schicht kann nicht von einer Verbesserung oder Verschlechterung der Performance
gesprochen werden, da sie vorher nicht existierte. Obwohl die Schicht noch neu ist, zeichnet sich
die Tendenz ab, dass sie im Bezug auf Performance nicht signifikant sein wird und das, obwohl sie
nicht optimiert ist, nachträglich eingebaut ist und zusätzliche Indirektionen durch Entwurfsmuster
enthält.
Durch die Optimierungen hat sich der Schwerpunkt der Schichten, in denen die Anwendung Zeit
verbringt, verlagert. Wie für eine Schicht mit Hilfsfunktionalität zu erwarten, nimmt die Util-
Schicht jetzt keinen so großen Stellenwert mehr ein. Dennoch ist ein Wert von 11 Prozent nicht we-
nig. Würde das Datenbankschema des JCommSy angepasst werden, so dass das (De)Serialisieren
des Extras-Felds entfallen könnte, würde die Util-Schicht von 18 auf weniger als 1 Sekunde Aus-
führungszeit fallen. Unter der vereinfachenden Annahme, dass die Datenbankanfrage selber durch
die zusätzlichen Spalten nicht langsamer wird, gilt folgendes: Hibernate könnte rechnerisch mehr
als 1.000 Felder an der ExtrasItemImpl-Klasse setzen, bevor dies mit der derzeitigen durchschnitt-
lichen Zeit, die durch die Rekursion des PHPDeserializers ausgelöst wird, gleichzieht. Die Re-
76 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
kursion ist in Kategorie III eingeordnet, die Zugriffe auf die einzelnen Felder liegen in der Mitte
von I, es liegt also wenigstens ein Faktor von 1.000 dazwischen. In welchen Grenzen die vereinfa-
chende Annahme gilt, kann im Rahmen dieser Arbeit nicht geklärt werden. Die Menge an Daten
wird durch die Aufteilung geringer, da weniger redundante Formatierungsinformationen benötigt
werden. Andererseits wird die Datenbank-Tabelle breiter, was Auswirkungen hat. Die Anzahl der
Aspekte die im Extras-Feld gespeichert werden, liegt bei weniger als 100.
Die Präsentationsschicht und die Service-Schicht machen zusammen über 75 Prozent der Ausfüh-
rungszeit aus. Sie werden im Wesentlichen durch die Verwendung der Rahmenwerke Servlet und
Spring sowie Hibernate für die Datenbankzugriffe ausgemacht. Dadurch sperren sie sich direk-
ter Optimierungen. Insgesamt ist die Performance durch Ein- und Ausgabe geprägt. 24 Prozent
der Ausführungszeit wird in Hibernate-Klassen verbracht und 33 Prozent werden durch die JSPs
ausgemacht. Weniger als 14 Prozent werden in JCommSy-Klassen verbracht. Der Wert von 14
Prozent enthält den Mehraufwand durch das Profiling. Der Faktor, um den das Profiling die An-
wendung ausbremst, ist abhängig von den Methoden die untersucht werden. Abschnitt 5.5.1 legt
ein Faktor von ca. 10 nahe. Dieser Faktor ist in den anderen Prozentangaben für Hibernate-Klassen
und JSPs nicht enthalten, da diese vom Profiling ausgeschlossen waren.
Pro Anfrage werden durchschnittlich 14 Zugriffe auf die Datenbank ausgeführt, was in durch-
schnittlich 33 ms Datenbankzeit pro Anfrage resultiert. Die restlichen 320 ms, die Hibernate-
Klassen pro Anfrage benötigen, liefert Hibernate die Daten aus dem Cache bzw. benötigt Hiber-
nate zum Verwalten der Objekte.
Bis auf die Rekursion zum Deserialisieren des Extras-Felds, gibt es keine aufwendigen Algorith-
men im JCommSy. Deshalb sind Kategorie IV-Aspekte entscheidend für die Gesamtperformance
und die Kategorie I-Aspekte, wie zusätzliche Schichten, haben keine Auswirkungen.
Um bezüglich der Service-Schicht weiter zu optimieren, könnten weitere Services-Methoden ge-
bündelt werden. Das Profil der unterschiedlichen Services ist jedoch flach. Das bedeutet, es müsste
an vielen unterschiedlichen Stellen optimiert werden und jede Optimierung könnte nur wenig zur
Verbesserung der Gesamtperformance beitragen.
Insgesamt lässt sich mit 61 Prozent eine signifikante Verbesserung der Performance ausmachen.
6.3.2 Architektur
Die Architektur wurde durch das Extrahieren der Fachlogik verbessert. Dieses Refactoring hat
das Verhalten der Anwendung nicht verändert, sondern nur die Fachlogik klarer von der Darstel-
lung getrennt. Das erleichtert die Weiterentwicklung beider Komponenten, da die Ausstatter nur
noch eine Funktionalität haben. Insgesamt ist die Soll-Architektur, wie sie in Abbildung 4.2 dar-
gestellt wird, ausgereifter und präziser. Neben der Verbesserung der Soll-Architektur ist auch die
Ist-Architektur verbessert worden. Im derzeitigen Entwicklungsstand gibt es keine Architektur-
verletzung mehr, d. h. die Abbildung 4.2 stellt gleichzeitig auch die Ist-Architektur dar.
Zusätzlich zur verbesserten Architektur ist die Dokumentation der Architektur besser geworden.
6.4 Zusammenfassung 77
Es liegt eine aktuelle Architekturbeschreibung vor und es gibt ein Modell der Architektur für den
Sotoarc. Damit kann die Architektur besser kommuniziert und überprüft werden.
6.4 Zusammenfassung
Die Analysen in den vorhergehenden Kapiteln haben mögliche Optimierungen identifiziert. Die
Optimierung bezüglich der Deserialisierung des Extras-Feld der Datenbank, hat gezeigt, dass der
erste Ansatz beim Optimieren nicht immer zielführend ist. Dadurch, dass die betroffene Klasse
um zusätzliche Logik erweitert wurde, kann das (De)Serialisieren auf das nötige Mindestmaß be-
grenzt werden. Dies verbessert die Performance und erhöht die Komplexität der Klasse.
Das Extrahieren der Fachlogik in eine eigene Schicht hat nicht zu einer Verschlechterung der Per-
formance geführt. Die Zuständigkeiten der Ausstatter haben sich durch die Umgestaltung deutlich
geschärft. Alternative Oberflächen können die Fachlogik des JCommSy nun wiederverwenden.
Die Verwendung des Spring-Mechanismus zur Erzeugung und Verwaltung von Objekten hat Aus-
wirkungen auf die Performance. Wiederholte Aufrufe zur Erzeugung eines CommsyProperties-
Objekts, resultieren in schlechter Performance. Die Architektur sieht vor, dass die Tags ein bereits
existierendes Objekt verwenden, statt ein neues zu erzeugen. Wäre dieser Architekturaspekt kla-
rer kommuniziert oder automatisiert überprüft worden, hätte die schlechte Performance verhindert
werden können.
Im JCommSy gibt es eine Datenbankanfrage, die innerhalb einer Schleife ausgeführt wird. Durch
das Erweitern des zugehörigen Services, können diese Datenbankanfragen gebündelt in einer An-
frage passieren. Dies verbessert die Performance der Anwendung.
Die Wahl des Ausführungsmodus der JVM bietet kein Potenzial zur Optimierung der Performance
des JCommSy. Weder das Aus- und Anschalten der Assertions, noch die Ausführung im Server-
Modus haben signifikante Auswirkungen auf die Performance.
Insgesamt ist die Architektur klarer strukturiert und einfacher zu kommunizieren. Die Performance
des JCommSy hat sich durch die Optimierungen stark verbessert. Die Ausführungszeit hat sich um
61 Prozent verringert.
78 Kapitel 6 Ergebnisse der Refactorings und Optimierungen am JCommSy
79
Kapitel 7
Allgemeine Schlussfolgerungen
In diesem Kapitel wird vom JCommSy abstrahiert und die gewonnenen Erkenntnisse auf allge-
meinerer Ebene dargestellt und bewertet. Es liegen nur Messergebnisse für ein System vor, so
dass nicht von einer repräsentativen Anzahl an Messungen gesprochen werden kann. Es wird be-
gründet, warum eine Abstraktion gerechtfertigt ist und deren Aussagekraft eingeschätzt.
Wenn im Folgenden die Formulierung „keine Performance-Auswirkungen“ verwendet wird, dann
bedeutet das, dass es keine für die Performance der Anwendung signifikanten Auswirkungen gibt.
Es kann minimale Auswirkungen geben, welche jedoch unterhalb normaler Abweichungen liegen,
die z. B. durch unterschiedlich lange Festplattenzugriffe entstehen.
7.1 Externe Bibliotheken
Es können unterschiedliche Bereiche innerhalb von Anwendungen ausgemacht werden, die Aus-
wirkungen auf die Performance und die Architektur haben, aber nicht von den Entwicklern des
Projekts geschrieben sind, sondern über eingebundenen Bibliotheken hinzukommen. Diese wer-
den in diesem Abschnitt vorgestellt.
7.1.1 Logging
Es besteht die Notwendigkeit zur Laufzeit Informationen über den Zustand einer Anwendung zu
erhalten. Gerade bei Web-Anwendungen, ist es für den Entwickler schwierig, im Fehlerfall an
zusätzliche Informationen zu kommen. Es bietet sich an, Logging-Rahmenwerke einzusetzen, um
derartige Informationen verfügbar zu machen. Sie bieten u. a. eine Abstufung in Log-Level wie
Warnung, Fehler oder Information. Damit ist es möglich, den Detaillierungsgrad einzustellen, wel-
che Informationen ausgegeben werden sollen.
Die Ausgabe der Informationen erfolgt in eine Datei oder wird über ein Netzwerk an einen an-
deren Rechner übertragen. In beiden Fällen sind dafür langsame Ausgabe-Operationen nötig.
Debug-Informationen können darum schnell zu einem Flaschenhals der Performance der Anwen-
dung werden. Die Log-Level sollten deshalb mit Bedacht gewählt werden, so dass der normale
Betrieb nicht durch unnötiges Logging gebremst wird. Die korrekte Verwendung des Loggers
ermöglicht es darüber hinaus für jede Klasse einzustellen, ob, und wenn ja, wie detailliert das
Logging für sie durchgeführt werden soll. Bereits während der Entwicklung sollten Richtlinien
festgelegt werden, wie das Logging zu benutzen ist. So ist es im Betrieb möglich, bei Bedarf
die Logging Funktionalität zu nutzen und andernfalls keine Performance-Nachteile zu erhalten.
80 Kapitel 7 Allgemeine Schlussfolgerungen
Deaktiviertes Logging verursacht keine signifikante Performance-Verschlechterungen gegenüber
einem System, das keine Logging-Anweisungen im Quelltext enthält. Der zusätzlich Aufwand bei
deaktiviertem Logging liegt im niedrigen Bereich der Kategorie I, im Gegensatz zu Kategorie IV
bei aktiviertem Logging.
7.1.2 Rahmenwerke allgemein
Rahmenwerke werden eingesetzt, um softwaretechnischen Ansprüchen wie Wiederverwendbar-
keit und Austauschbarkeit zu genügen und bestimmte Strukturen oder Funktionalitäten nicht selbst
implementieren zu müssen. Ein Rahmenwerk wie Hibernate befreit die Entwickler davon, ihr Pro-
gramm für eine spezielle Datenbank zu schreiben. Wird auf einen OR-Mapper aufgesetzt, kann
die eigentliche Datenbank ausgetauscht werden, ohne dass die Anwendung geändert werden muss.
Die Kapselung, die ein OR-Mapper erreicht, geht so weit, dass der Entwickler unter Umständen
nicht mehr weiß, wann das Rahmenwerk Datenbankzugriffe durchführt.
Ein Rahmenwerk wie Spring stellt z. B. den Dependency Injection Mechanismus bereit (siehe
2.4.6). Es ist für die Verwaltung und Erzeugung von Objekten zuständig. Die Allgemeinheit des
Rahmenwerks sorgt gleichzeitig für zusätzliche Berechnungen und Überprüfungen. Beispielswei-
se müssen Aspekte abgeprüft werden, die in der betrachteten Anwendung niemals auftreten, aber
in anderen Anwendungen auftreten können. Hier kostet Flexibilität Performance.
Ob die Verwendung von Rahmenwerken die Performance einer Anwendung verbessert oder ver-
schlechtert, kann im Rahmen dieser Arbeit nicht beantwortet werden. Einerseits sind Performance-
Einbußen aufgrund der Generizität des Rahmenwerks zu erwarten, andererseits ist ein Rahmen-
werk potentiell besser auf Performance optimiert als eine Eigenentwicklung. Beispielsweise bietet
Hibernate Caching-Mechanismen und explizite Schnittstellen um Einfluss auf die Performance zu
nehmen.
Es lässt sich jedoch feststellen, dass die Verwendung von Rahmenwerken die Performance vor
dem Entwickler verschleiert. Die Untersuchung mit dem Profiler zeigte, dass es die Rahmenwer-
ke sind, in denen signifikant Zeit verbracht wird. Für den Entwickler steht der Quelltext dieser
Rahmenwerke im Allgemeinen nicht zur Verfügung, genausowenig wie die Möglichkeit, direkt
Einfluss auf das Rahmenwerk zu nehmen. Seine Möglichkeiten zu Optimieren begrenzen sich
darauf, was von den Entwicklern des Rahmenwerks vorgesehen ist. Ein Beispiel hierfür ist der
externe Cache von Hibernate.
Bezüglich der Architektur hat die Verwendung von Rahmenwerken sowohl positive als auch nega-
tive Aspekte. Durch die Rahmenwerke werden eine klarere Struktur und bestimmte Vorgehenswei-
sen vorgegeben. Das Spring-Rahmenwerk legt beispielsweise nahe, dass Objekte per Dependency
Injection und von Spring erzeugt werden, anstatt das der Entwickler sie selber erzeugt.
Andererseits kann die Komplexität der Architektur steigen und sie wird dadurch schwieriger zu
kommunizieren. Bestimmte Funktionalitäten der Anwendung werden vor dem Entwickler ver-
borgen und passieren scheinbar automatisch. Für Entwickler ist ein Rahmenwerk in der Regel
eine Black-Box, also ein geschlossenes Gebilde, von dem er nur das Äußere (also die Schnitt-
7.2 Vorgehen beim Optimieren 81
stelle) kennt. Durch die falsche Benutzung eines Rahmenwerks kommen weitere Fehlerquellen
hinzu. Dies trifft umso mehr zu, je weniger ein Entwickler das Rahmenwerk kennt. Er hat nicht
die Möglichkeit, das was passiert direkt im Quelltext nachzuvollziehen, sondern muss die in der
Dokumentation beschriebenen allgemeinen Konzepte verstehen.
7.2 Vorgehen beim Optimieren
In diesem Abschnitt wird vorgestellt, was beim Optimieren beachtet werden muss und wie vorge-
gangen werden sollte. Zusätzlich wird die Wichtigkeit von Performance als Qualitätseigenschaft
dargestellt.
7.2.1 Optimierungen durch Messungen belegen
Bereits bei einfachen Anwendungen ist es schwierig, korrekte Aussagen über deren Performance
zu machen, bzw. diese zu verbessern, ohne dass dies durch Messungen untermauert wird. Bei
einer komplexen Web-Anwendung, die in einer Hochsprache wie Java geschrieben ist, die in ei-
ner virtuellen Maschine läuft und Rahmenwerke einsetzt, ist es noch deutlich schwieriger, das
Performance-Verhalten vorauszusagen. Deswegen ist es unvermeidbar, jede vermeintliche Opti-
mierung durch Messungen zu belegen. Dies gilt bereits für kleine Optimierungen, die nur eine
Klasse oder Methode betreffen.
7.2.2 In den hohen Kategorien zuerst optimieren
Großes Optimierungspotential liegt in den höheren Kategorien. Dort kann bereits die Optimierung
innerhalb einer Kategorie signifikante Auswirkungen haben (siehe 5.4.1). Gibt es anstelle von
zwei nur noch einen Datenbankzugriff, ist die Performance zwar besser geworden, jedoch immer
noch in der gleichen Kategorie einzuordnen.
Besonders in der Kategorie V haben Optimierungen bereits innerhalb der Kategorie schnell große
Auswirkungen. In Kategorie IV kann es sich besonders lohnen, daraufhin zu optimieren, die An-
zahl der Ein- und Ausgabe-Zugriffe zu verringern. So kann z. B. durch das Zwischenspeichern
des Inhalts einer Datei, die Performance von Kategorie IV nach III oder II verbessert werden.
In den Kategorien IV und V, sowie zum Teil auch in III, kann bereits während der Entwick-
lung der Performance-Aspekt im Fokus behalten werden. Andererseits sollten vermeintliche Opti-
mierungen in den niedrigen Kategorien nicht schon während der Entwicklung eingebaut werden.
Beispielsweise sollte vermieden werden, eine Variable vor einer Schleife zu deklarieren, um sie
im Schleifenkörper nur noch zuweisen zu müssen. Dies ist vermeintlich schneller, für die Per-
formance der Anwendung aber nicht signifikant. Die Änderung hat, wenn überhaupt, nur Aus-
wirkungen innerhalb von Kategorie I. Andererseits macht sie den Quelltext fehleranfälliger und
schwieriger zu verstehen, da die Variable einen größeren Gültigkeitsbereich hat, als nötig (siehe
[BGWA08]).
82 Kapitel 7 Allgemeine Schlussfolgerungen
Donald Knuth hat bereits 1974 festgestellt, dass verfrühtes Optimieren Gefahren birgt:
„We should forget about small efficiencies, say about 97% of the time: premature
optimization is the root of all evil.“ [Knu74]
Dies schließt nicht aus, dass auch innerhalb von Kategorie I optimiert werden kann, allerdings darf
dies nur in Form einer disziplinierten Optimierung mit Messungen geschehen und nur dann, wenn
gezeigt ist, dass Optimierung nötig ist. Nur so kann verhindert werden, dass die Performance nicht
versehentlich verschlechtert oder unnötig Komplexität hinzugefügt wird.
7.2.3 Flaches Profil anstreben
Die Untersuchung hat gezeigt, dass es zunächst eindeutig war, welche Bereiche der Anwendung
für die Performance ausschlaggebend waren. Es gab wenige Methoden, die viel Leistung bean-
spruchten und viele Methoden, die wenig Leistung beanspruchten. Dieses „steile“ Performance-
Profil ist eine typische Ausgangslage (siehe 2.2.7) und ermöglicht gezielte Optimierung. Die ers-
ten Optimierungen bringen große Performance-Verbesserungen. Durch die Optimierungen wird
das Profil zunehmend flacher, d. h. es gibt nicht mehr wenige Methoden, die viel Rechenzeit be-
anspruchen, sondern viele Methoden, die wenig Rechenzeit beanspruchen. Es ist von Anwendung
zu Anwendung unterschiedlich, wann das Profil als flach bezeichnet wird und inwiefern es dann
immer noch einzelne langsame Methoden gibt, die nicht weiter optimiert werden können. Je fla-
cher das Profil wird, desto schwieriger wird es, weiter zu optimieren und desto kleiner werden die
Verbesserungen, die erreicht werden können. Liegen keine harten Anforderungen bezüglich der
Performance vor, sollte man bei einem flachen Profil aufhören weiter zu optimieren.
In der Regel sind die ersten Optimierungen bezüglich der zu investierenden Zeit, der hinzukom-
menden Komplexität und der erreichten Verbesserung lohnenswert. Auch für Anwendungen mit
nur schwachen Performance-Anforderungen, lohnt sich ein Profiling, weil sich bereits mit gerin-
gen Investitionen deutliche Verbesserungen erreichen lassen.
7.2.4 Performance ist wichtig
Die Performance ist eine wichtige Qualitätseigenschaft von Software (siehe 2.2.4), die nicht ver-
nachlässigt werden sollte. Durch die zunehmend leistungsfähigeren Computer, rückt dieser Aspekt
aus dem Fokus von Softwareentwicklern. Gerade in modernen Hochsprachen wie Java, ist die Per-
formance einfach und nicht-invasiv messbar. Dies sollte genutzt werden und Performance wieder
mehr in den Mittelpunkt gerückt werden. Es sollte das Ziel sein, das disziplinierte Performance-
Betrachtungen und Optimierungen zum Bestandteil des Entwicklungsprozesses werden. Das be-
deutet, dass die Entwickler den Quelltext weiterhin mit dem Fokus auf Korrektheit und Lesbarkeit
schreiben. In einem extra Schritt wird auf Grundlage von Messungen die Performance untersucht
und gegebenenfalls optimiert.
7.3 Auswirkungen von Performance-Optimierungen auf die Architektur 83
7.3 Auswirkungen von Performance-Optimierungen auf dieArchitektur
Obwohl sich eine Performance-Optimierung zunächst auf den Quelltext auswirkt, kann sie auch
Auswirkungen auf die Architektur haben. In diesem Abschnitt werden diese Auswirkungen dar-
gestellt.
7.3.1 Optimierungen erhöhen die Komplexität
Wird ein Flaschenhals in einer Anwendung identifiziert und entsprechend optimiert, so hat dies
unmittelbar Auswirkungen auf den Quelltext. Teilweise beschränken sich die Änderungen auf ei-
ne Klasse oder eine Methode und haben damit nur auf kleiner Ebene Auswirkungen. Sie erhöhen
dann höchstens die Komplexität einer einzelnen Klasse. Die erhöhte Komplexität kann sich darin
zeigen, dass der Quelltext schwieriger und schlechter zu lesen ist. Dies verletzt die Anforderung
an den Quelltext, dass er das was er tut auch kommuniziert. Eine Klasse, die Ergebnisse über
mehrere Methodenaufrufe hinweg zwischenspeichert, kann schwieriger zu verstehen und weiter-
zuentwickeln sein. Ein einfacher aber langsamer Algorithmus kann durch einen schnelleren, kom-
plizierteren Algorithmus ersetzt werden.
Zum Teil sind mehrere Klassen unterschiedlicher Schichten und deren Zusammenspiel von einer
Optimierung betroffen. Ist das der Fall, kann die Optimierung die Komplexität auf Architektur-
ebene betreffen. Werden beispielsweise in einer Schicht Objekte zwischengespeichert und wie-
derverwendet, dann ist diese Information Teil der Architektur und muss kommuniziert werden. Es
hat Auswirkungen für benutzende Schichten, wenn sie Objekte nicht exklusiv erhalten, sondern
gemeinsam verwenden. Beispielsweise muss damit gerechnet werden, dass sich der Zustand eines
Objektes ändert und damit entsprechend umgegangen werden.
Würde mit einer Optimierung zusätzliche Komplexität in die Architektur gebracht werden, muss
besonders sorgfältig abgewogen werden, welcher Aspekt wichtiger ist.
Nicht alle Optimierungen erhöhen die Komplexität, zum Teil wird sie nicht einmal auf Klassen-
ebene erhöht. Eine Optimierung kann die Komplexität verringern. Am Beispiel des JCommSy
konnte jedoch keine Performance-Optimierung gefunden werden, deren direkte Auswirkung eine
Senkung der Komplexität war. Dies könnte z. B. erreicht werden, wenn überflüssige Generizität
entfernt wird. Agile Entwicklungsmethoden, wie z. B. Extreme Programming (siehe [BA04]), ver-
folgen den Ansatz, stets die einfachste Lösung zu wählen und keine Funktionalität auf Vorrat zu
entwickeln. Sie wirken damit der Notwendigkeit von derartigen Optimierungen entgegen.
Durch Optimierungen können Architekturverletzungen aufgedeckt werden, insbesondere wenn sie
in schlechter Performance resultieren. Die Optimierung bewirkt dann gleichzeitig die Beseitigung
einer Architekturverletzung (siehe auch 7.4.3), was indirekt die Komplexität der Anwendung re-
duzieren kann.
84 Kapitel 7 Allgemeine Schlussfolgerungen
7.4 Auswirkungen von sauberer Architektur auf die Performance
Im vorherigen Abschnitt wurden die Auswirkungen von Optimierungen auf die Architektur dis-
kutiert. Im Folgenden wird die andere Richtung betrachtet. Es wird gezeigt, wie sich saubere
Architektur und die Einhaltung von softwaretechnischen Prinzipien auf Performance auswirkt.
7.4.1 Ausbau von Schichtung oder Einführung von Entwurfsmustern
Sowohl Architektur als auch die Verwendung von Entwurfsmustern resultiert in zusätzlichen In-
direktionen. Die Einführung eines Entwurfsmusters resultiert beispielsweise in zusätzlichen Ob-
jekterzeugungen und Methodenaufrufen (siehe auch 5.4).
Für eine existierende, komplexe Anwendung hat weder das Hinzufügen einer Schicht, noch die
Verwendung von zusätzlichen Entwurfsmustern Auswirkungen auf die Performance. Ein- und
Ausgabe-Operationen oder aufwendige Berechnungen benötigen im Verhältnis deutlich mehr Re-
chenleistung, so dass die zusätzlichen Indirektionen keine Auswirkungen haben (siehe Abbildung
5.3).
Indirektionen durch Architektur und Entwurfsmuster haben Auswirkungen im Bereich von Na-
nosekunden, also in Kategorie I (siehe 5.4.1). Bereits Standardfunktionalitäten, wie der Aufruf
von toString, liegen eine Kategorie darüber (II). Aufwendige Methoden, die z. B. Schleifen oder
Rekursionen enthalten, sind im Bereich von 100 Mikrosekunden (Kategorie III) zu finden. Ein-
und Ausgabe-Operationen wie Netzwerkkommunikation oder Dateizugriffe verursachen Zeiten in
Kategorie IV (Millisekunden). Deshalb gilt zusammenfassend: Für nicht triviale Anwendungen
haben Indirektionen durch Architekturen und Entwurfsmuster keine Auswirkungen auf die Per-
formance.
Falls die Performance einer Anwendung durch das Hinzufügen einer Schicht schlechter wird, dann
liegt das daran, dass die Funktionalität der Schicht, bezüglich der Performance, signifikant ist.
Wird eine aufwendige Berechnung aus einer Schicht in eine neue Schicht verschoben, dann kann
die neue Schicht einen signifikanten Teil der Performance ausmachen. Da vergleichbare Berech-
nungen allerdings in der anderen Schicht nicht mehr ausgeführt werden, wird dort entsprechend
eine Verbesserung der Performance festzustellen sein. Die Tatsache, dass eine Berechnung in eine
eigene Schicht verlegt wird, bzw. mit Entwurfsmustern realisiert ist, hat keine Auswirkungen auf
die Performance der Anwendung. Für die Performance ist es unerheblich, wo die aufwendigen Be-
rechnungen liegen. Die softwaretechnischen Vorteile, die durch Kapselung in eine eigene Schicht
entstehen, können also voll ausgenutzt werden.
In dieser Arbeit wurde eine bestehende Anwendung weiterentwickelt. Es wurde nicht untersucht,
ob die initiale Entscheidung über die Architektur der Anwendung Auswirkungen auf die Perfor-
mance hat. Die Ergebnisse legen jedoch den Schluss nahe, dass dies nur signifikant ist, solange die
Anwendung trivial ist. Sobald die Anwendung Kategorie IV oder V Aspekte enthält, verschiebt
die Performance sich so drastisch, dass Indirektionen durch Architektur keine Rolle mehr spielen.
7.4 Auswirkungen von sauberer Architektur auf die Performance 85
7.4.2 Softwaretechnische Prinzipien
Das Geheimnisprinzip gibt vor, das der Zugriff auf die interne Datenstruktur unterbunden wird
[Z+98, S. 23]. Die Datenkapselung resultiert in Getter- und Setter-Methoden, anstatt von Direkt-
zugriffen auf die Variablen. Der Mehraufwand liegt in Kategorie I und ist nicht signifikant.
Die Umsetzung des Vertragsmodells erfordert das Überprüfen von Vor- und Nachbedingungen
[Mey92]. Die dafür nötigen Berechnungen sind in der Regel in Kategorie I einzuordnen. Ab-
schnitt 6.2 zeigt am Beispiel des JCommSy, wie gering die Auswirkungen sind.
Klassen sollen eine hohe Kohäsion haben und möglichst lose mit anderen Klassen gekoppelt sein
[Z+98, S. 43]. Dies bedeutet, dass jede Einheit eine abgeschlossene logische Aufgabe hat. Auch
das sorgt für zusätzliche Indirektionen, z. B. wenn eine bestimmte Teilaufgabe von einer anderen
Klassen erfüllt wird. Wiederum sind diese Aspekte in der Kategorie I einzuordnen.
Entwurfsmuster sind bewährte Lösungs-Schablonen und die softwaretechnische Prinzipien beach-
ten [GHJV95]. Ihre Auswirkungen auf die Performance sind in die Kategorie I einzuordnen (siehe
7.4.1). Sie, genauso wie die Prinzipien, sollten nicht aus Performance-Gründen missachtet wer-
den. Wird festgestellt, dass die Performance nicht ausreichend ist und optimiert werden muss,
erlaubt die hohe Kohäsion einer Klasse, das gezielt und einzelne Klassen betreffend optimiert
werden kann. Das Geheimnisprinzip ermöglicht es, eine Klasse zu optimieren, indem z. B. intern
eine andere Struktur gewählt wird. Wäre diese von außen sichtbar, könnte sie nur dann optimiert
werden, wenn alle benutzenden Klassen angepasst würden. In einer komplexen Anwendung mit
Kategorie IV oder V-Aspekten, verschlechtert der Einsatz von softwaretechnischen Prinzipien die
Performance nicht. Ihre softwaretechnischen Vorteile, zusammen mit den daraus resultierenden
besseren Möglichkeiten zu optimieren, rechtfertigen ihre Einhaltung.
7.4.3 Performance durch Einhaltung der Architektur
Eine der Aufgaben einer Architektur ist es, Zuständigkeiten festzulegen. Wenn das nicht ausrei-
chend passiert, kann eine Verschlechterung der Performance die Folge sein. Beispielsweise können
teure Methoden öfter als nötig ausgeführt werden, wenn den Entwicklern nicht bekannt ist, dass
sie bereits ausgeführt wurden und deren Ergebnisse vorliegen. Legt die Architektur Zuständigkei-
ten fest, kann dies positive Auswirkungen auf die Performance haben. Eine Voraussetzung dafür
ist, dass die Architektur ausreichend kommuniziert wird. Weiß ein Entwickler nicht, dass eine an-
dere Komponente für etwas zuständig ist, wird er diese nicht benutzen. Gibt es zusätzlich keine
ausreichende Überprüfung der Architektur, dann wird nicht aufgedeckt, dass eine Architekturver-
letzung vorliegt und die Verschlechterung der Performance nicht verhindert.
Die Architektur kann die korrekte Verwendung von Rahmenwerken fordern und fördern. Sieht die
Architektur vor, dass die Datenbankzugriffe durch eine Service-Schicht gekapselt sind, gibt es nur
noch sie als zentrale Stelle, in der die korrekte Benutzung des Rahmenwerks sichergestellt sein
muss. Das führt dazu, dass eine Reihe von Performance-Konzepten automatisch Einzug in die
Anwendung erhalten. Der Auf- und Abbau einer Datenbankverbindung ist in Kategorie IV und
86 Kapitel 7 Allgemeine Schlussfolgerungen
damit langsam. Durch die Kapselung der Datenbankzugriffe in eine Schicht und die Verwendung
von Hibernate muss sich die Anwendung beispielsweise nicht darum kümmern, dass Datenbank-
verbindungen bereitgehalten und wiederverwendet werden (siehe [BHRS07, S. 96 ff.]). Hibernate
übernimmt das und sorgt damit für eine Optimierung der Anwendung.
Eine klare Architektur, die kommuniziert und deren Einhaltung überprüft wird, hat positive Aus-
wirkungen auf die Performance einer Anwendung.
7.5 Datenbanken
Der Zugriff auf eine Datenbank ist verhältnismäßig langsam (Kategorie IV). Zum einen muss
eine Kommunikation zwischen unterschiedlichen Prozessen stattfinden, zum anderen muss die
Datenbank potentiell Ein-/Ausgabe-Operationen durchführen. Noch verstärkt wird das, wenn die
Datenbank auf einem anderen Rechner läuft und so Netzwerklatenzen hinzukommen. Der Zugriff
auf eine Datenbank ist somit eine performance-relevante Operation. In diesem Abschnitt werden
Ergebnisse vorgestellt, die den Zugriff auf die Datenbank betreffen.
7.5.1 Zugriffe bündeln
Die Untersuchungen haben gezeigt, dass durch das Bündeln von Datenbankzugriffen die Perfor-
mance verbessert werden kann. Es kann sinnvoll sein, statt in einer Schleife jeweils eine Informa-
tion aus der Datenbank zu holen, bereits vor der Schleife alle benötigten Informationen in einem
Datenbankzugriff zu holen. Wenn die Datenbankzugriffe durch eine Service-Schicht gekapselt
sind, dann müssen die Services entsprechendes leisten. Bieten sie keine Schnittstelle an, um ein
Bündel von Informationen auf einmal abzurufen, ist der Entwickler gezwungen, iterativ für alle
Informationen einen Aufruf an den Services zu machen. Die Performance einer Anwendung lässt
sich also steigern, wenn die Schnittstelle der Service-Schicht an die Art der Anfragen angepasst
wird. Dadurch wird die Service-Schicht um ein Stück anwendungsspezifische Logik erweitert,
geht also über eine einfache CRUD-Schnittstelle hinaus (siehe 4.4.1). Inwiefern es erlaubt und
gewünscht ist, dass die Service-Schicht diese Logik enthält, ist eine Architekturfrage.
Falls von vornherein klar ist, dass die Informationen immer als Bündel abgerufen werden, kann die
Methode bereits von Anfang an so entworfen werden, dass sie dieses Bündel zurückgibt. Andern-
falls sollte der Service erst dann um solche Methoden erweitert werden, wenn Profilings gezeigt
haben, dass bestimmte Informationen häufig unmittelbar nacheinander abgerufen werden und dies
in der derzeitigen Situation einen Flaschenhals darstellt.
Das Anpassen der Schnittstelle kann auf zwei Arten umgesetzt werden. Die Schnittstelle kann um
eine Methode erweitert werden oder es kann eine existierende Methode so angepasst werden, dass
sie das Bündel an Informationen liefert. Ersteres bietet sich an, wenn es auch Zugriffe gibt, bei
denen nur einzelne Informationen erfragt werden. Zweiteres bietet sich an, wenn die Informatio-
nen immer gemeinsam benötigt werden. Der Entwickler hat dann nicht mehr die Möglichkeit, den
Service aus Performance-Sicht falsch zu benutzen.
7.5 Datenbanken 87
7.5.2 OR-Mapper
Datenbankanfragen liegen in Kategorie IV. Zu einer Datenbankanfrage gehört die Kommunikati-
on mit der Datenbank und das Abarbeiten der Anfrage in der Datenbank. Auf Anwendungsseite
wird dafür zunächst eine SQL-Anweisung erzeugt und nachdem das Ergebnis der Datenbankan-
frage vorliegt, dieses wieder in entsprechende Objekte verpackt, die Teil der Anwendungslogik
sind. Der zeitaufwendige Aspekt ist die Kommunikation. Abhängig von der Anfrage und ob die
Daten bereits im Hauptspeicher sind, kann auch die Bearbeitung innerhalb der Datenbank zeitauf-
wendig sein. Das Befüllen der Objekte mit den erhaltenen Daten ist in der Regel in Kategorie I
einzuordnen.
Wird in einer Anwendung ein OR-Mapper, wie Hibernate, eingesetzt, dann passiert das Generie-
ren der SQL-Anweisung und das abschließende Übertragen der Ergebnisse in die Objekte automa-
tisch. Zusätzlich werden einige Verwaltungsaufgaben anfallen, wie z. B. die Registrierung eines
Objektes im Cache. Diese Verwaltungsaufgaben sind II oder III-Aspekte und von den Entwicklern
des OR-Mappers auf Performance optimiert. Auch beim Einsatz eines OR-Mappers bleibt die ei-
gentliche Anfrage an die Datenbank der für die Performance ausschlaggebende Teil. Dadurch dass
ein OR-Mapper Anfragen z. B. durch Caching spart oder speziell für eine Datenbank optimiert,
kann die Performance gesteigert werden.
Für die Architektur bedeutet der Einsatz eines OR-Mappers, dass die Entwickler von den Eigenhei-
ten der Datenbank befreit werden. Es fällt in den Aufgabenbereich von Hibernate, in den speziellen
SQL-Dialekt der jeweiligen Datenbank zu übersetzen. Der Entwickler kann seine Anfragen über
eine objektorientierte Programmierschnittstelle formulieren (siehe auch 2.4.5). Der Zugriff auf die
Datenbank wird durch den OR-Mapper gekapselt und die Datenbank dadurch austauschbar.
7.5.3 Datenbankschema
Ein zu schwach strukturiertes Datenbankschema kann die Performance einer Anwendung negativ
beeinflussen. Das Datenbankschema der untersuchten Anwendung sieht eine Spalte vor, in der
zusätzliche Informationen in Textform gespeichert werden. Da dieses Feld für unterschiedliche
Informationen genutzt wird, muss der Inhalt von einem Parser in die einzelnen Informationen zer-
legt werden. Dieser Vorgang verschlechtert die Performance der Anwendung.
Eine Anpassung des Schemas derart, dass für jede Information eine eigene Spalte oder Tabelle
im Schema vorgesehen wäre, würde das Parsen überflüssig machen und dadurch die Performance
verbessern. Des Weiteren würde die Datenbank weniger redundante Informationen enthalten, wie
sie beispielsweise vom Parser benötigt werden, um die Informationen trennen zu können.
Die Auswirkungen eines saubereren Datenbankschemas auf die Datenbank selber und damit ein-
hergehende Performance-Verbesserungen oder -Verschlechterungen liegen aber nicht im Fokus
dieser Arbeit.
88 Kapitel 7 Allgemeine Schlussfolgerungen
7.6 Zusammenfassung
Die Ergebnisse der Optimierungen, sowohl bezüglich der Performance, als auch hinsichtlich der
Architektur, werden in diesem Kapitel vom JCommSy abstrahiert.
Die Verwendung von Rahmenwerken kann positive und negative Auswirkungen auf die Perfor-
mance einer Anwendung haben. Rahmenwerke kapseln für die Performance relevante Aspekte.
Dadurch wird es für Entwickler schwieriger die Performance zu betrachten und zu optimieren,
wenn dies nicht explizit durch das Rahmenwerk unterstützt wird.
Beim Optimieren sollte stets abgewogen werden, ob weitere Optimierungen nötig sind. Ist das
Performance-Profil bereits flach, muss viel Aufwand für wenig zusätzliche Performance investiert
werden. Gute Performance kann die Benutzbarkeit einer Anwendung erhöhen und sollte ein Ziel
bei Softwareentwicklung sein.
Performance und Architektur sind nicht unabhängig voneinander. Die Komplexität der Architek-
tur kann durch Performance-Optimierungen erhöht werden. Andererseits können Optimierungen
Architekturverletzungen aufdecken und dadurch helfen die Architektur zu verbessern. Legt die
Architektur die Zuständigkeiten klar fest, kann dies Performance-Probleme verhindern, wie sie
z. B. durch Doppelberechnung oder falsche Benutzung von Rahmenwerken entstehen können.
Komplexe Architekturen und die Verwendung von Entwurfsmustern resultieren in zusätzlichen In-
direktionen. Diese haben auf die Gesamtperformance einer nicht trivialen Anwendung keine Aus-
wirkungen. Die Performance wird in solchen Anwendungen maßgeblich durch Ein- und Ausgabe-
Operationen oder aufwendige Berechnungen bestimmt. Deren Ausführungszeiten liegen mehrere
Kategorien über der, der Indirektionen.
Der Zugriff auf Datenbanken ist vergleichsweise langsam und hat Auswirkungen auf die Per-
formance. Durch das Bündeln von Datenbankanfragen zu einer Anfrage, kann die Performance
gesteigert werden. Wird das Datenbankschema aufgeweicht, so dass einzelne Felder unterschiedli-
che Informationen enthalten, kann dies aufwendiges Auseinanderrechnen der Informationen nötig
machen.
89
Kapitel 8
Schlussbemerkungen
In diesem Kapitel werden die wesentlichen Punkte dieser Arbeit zusammengefasst und die Arbeit
abgeschlossen. Im Ausblick werden offen gebliebene Aspekte und Fragen beschrieben und somit
mögliche Ansatzpunkte für zukünftige Arbeiten gegeben.
8.1 Fazit
Das JCommSy konnte im Laufe dieser Arbeit, sowohl bezüglich der Architektur, als auch der
Performance, verbessert werden. Durch die durchgeführten Refactorings ist die Architektur klarer
geworden. Die Zuständigkeiten sind deutlicher und die Entkopplung der Darstellungslogik von
der Fachlogik vorangetrieben worden. Durch die Optimierungen konnte die Performance für die
untersuchten Testfälle um ca. 61 Prozent gesteigert werden. Die meisten der Optimierungen waren
auf kleiner Ebene und benötigten lediglich lokale Änderungen. Einige hatten Auswirkungen auf
die Architektur, sie haben diese jedoch nicht verschlechtert und keine Verletzungen hinzugefügt.
Die Analysen haben gezeigt, dass bei einer Web-Anwendung eine ausgeprägte Architektur und
die Performance nicht im Widerspruch stehen, sondern einander ergänzen und sowohl positive als
auch negative Auswirkungen aufeinander haben. Die Performance-Skala lässt sich in Kategorien
unterteilen. Indirektionen liegen in Kategorie I, während Datenbank- und Festplattenzugriffe in
IV liegen. Die durch eine ausgeprägte Architektur oder durch den Einsatz von Entwurfsmustern
entstehenden Indirektionen sind für die Gesamtperformance einer Web-Anwendung ohne Bedeu-
tung. Sie fallen neben den Kategorie IV und V-Aspekten nicht ins Gewicht.
Die Architektur wirkt in anderen Bereichen auf die Performance ein. Sie legt Zuständigkeiten fest
und strukturiert die Anwendung, so dass aufwendige Mehrfachberechnungen vermieden werden
können und die korrekte Benutzung von Rahmenwerken sichergestellt werden kann. Performance-
Optimierungen wirken auf den Quelltext der Anwendung meist in Form von Erhöhung der Kom-
plexität, wobei dies zum Teil auch die Architekturebene betrifft.
Ähnlich wie bei der Analyse der Architektur braucht man auch für die Betrachtung von Perfor-
mance Software-Werkzeuge. Mit diesen ist es möglich, die Performance einer Anwendung zu un-
tersuchen und Schwachstellen zu identifizieren. Zunächst können diese Schwachstellen, wenn sie
einmal identifiziert worden sind, relativ einfach und mit großen Performance-Vorteilen optimiert
werden. Mit zunehmender Anzahl an durchgeführten Optimierungen, wird das Kosten-Nutzen-
Verhältnis jedoch schlechter.
Gerade weil die ersten Optimierungen günstig sind, sollten Performance-Analysen zum Entwick-
lungsprozess dazu gehören. Performance ist eine wichtige Qualitätseigenschaft von Software und
90 Kapitel 8 Schlussbemerkungen
sollte wieder mehr in den Fokus gerückt werden. Kleine, undiszipliniert durchgeführte Optimie-
rungen während des Programmierens bleiben allerdings weiterhin die „Wurzel allen Übels“ (siehe
7.2.2). Die Performance einer komplexen Anwendung kann kaum vorhergesagt werden, so dass
die Optimierungen immer durch Messungen zunächst gerechtfertigt und danach belegt werden
müssen.
8.2 Ausblick
Für die unterschiedlichen Erkenntnisse dieser Arbeit, wurde in unterschiedlichen Bereichen gear-
beitet. Nicht alle konnten dabei in gleichem Umfang bearbeitet werden. Das betrifft besonders den
Begriff Performance und dessen zahlreiche Ausprägungen. Auch bezüglich Architektur wurde nur
ein Ausschnitt bearbeitet. In diesem Ausblick werden deshalb offene Fragen und Ansatzpunkte für
zukünftige Arbeiten vorgestellt.
Im Laufe des Refactorings „Fachlogik extrahieren“ wurde der Aufgabenbereich der Ausstatter ge-
schärft. Die Architektur sieht jetzt vor, dass Ausstatter die Items nicht mehr bearbeiten. In Java ist
es nicht möglich, zuzusichern, dass ein Objekt von einem anderen nicht verändert wird. Da dies
jedoch gewünscht ist, muss nach Alternativen gesucht werden. Die Items könnten den Ausstat-
tern unter einer Schnittstelle zur Verfügung gestellt werden, die nur einen Teil der eigentlichen
Schnittstelle enthält, nämlich nur die Methoden, die die Objekte nicht verändern. Analog dazu,
sollte auch die ItemEditBean nicht von den Ausstattern verändert werden.
Darüber hinausgehend, sollte es überprüfbare Regeln geben, die sicherstellen, dass aus den JSPs
nur auf Fragmente zugegriffen wird und nicht auf die ItemEditBean oder andere Klassen. Eine
solche Regel würde die Einhaltung der Architektur sicherstellen.
Es konnte nicht die gesamte Fachlogik extrahiert werden. Im Laufe zukünftiger Weiterentwicklun-
gen am JCommSy können weitere Aspekte auftauchen, die in die Logik-Schicht gehören. Durch
gezielte und aufwendige manuelle Suche ließen sich diese direkt finden und extrahieren.
Die Erkenntnisse bezüglich der Auswirkungen der Veränderungen der Architektur auf die Perfor-
mance beziehen sich in dieser Arbeit immer auf eine bereits bestehende, ausgeprägte Architektur.
Inwiefern und mit welchen Einschränkungen sie sich auch auf die initiale Architektur einer An-
wendung übertragen lassen, kann hier nicht geklärt werden.
Die Eigenschaft Performance hat viele Bereiche. In dieser Arbeit wurde der Fokus auf die Ausfüh-
rungszeit gelegt. Aufgrund des zeitlichen Rahmens der Arbeit, wurde der Aspekt Speicherauslas-
tung nur kurz behandelt. Bei genauerer Betrachtung könnte untersucht werden, ob Speicherlecks
vorliegen. Durch Speicherlecks steht der Anwendung mit zunehmender Laufzeit weniger Speicher
zur Verfügung als eigentlich vorhanden ist. Die daraus folgenden häufigeren Einsätze des Garbage
Collectors resultieren wiederum direkt in schlechterer zeitlicher Performance.
Offen bleibt die Frage inwiefern Speicher und Zeit miteinander zusammenhängen. In den un-
tersuchten Beispielen, ging eine Reduzierung der Ausführungszeit mit weniger Speicherumsatz
einher. Dies müsste genauer quantifiziert werden, indem z. B. für den Speicher Kategorien einge-
8.2 Ausblick 91
führt werden bzw. die Kategorisierung um eine zweite Dimension erweitert wird.
Moderne JVMs können das Garbage Collecting nach unterschiedlichen Strategien durchführen.
Welche Strategie für eine Web-Anwendung, wie das JCommSy, die beste ist und wie groß der Ge-
samtvorteil ist, der sich durch die richtige Wahl erzielen lässt, muss an anderer Stelle untersucht
werden. Ebenso kann die Wahl der JVM und des Servlet-Containers betrachtet werden. Alle diese
Aspekte verlassen jedoch den Bereich Software-Architektur und zielen mehr auf die Optimierung
des laufenden JCommSy hin. Das Gleiche gilt für die Wahl des Datenbanksystems und für Opti-
mierungen der Datenbank auf das JCommSy.
Es könnte versucht werden, die Datenbank zu optimieren, z. B. indem sie auf das Benutzungsprofil
durch das JCommSy abgestimmt und konfiguriert wird oder eine alternative Datenbank verwendet
wird. Das Umsteigen von einem relationalen auf ein objektrelationales oder ein objektorientiertes
Datenbankmodell hat Auswirkungen, sowohl auf die Performance, als auch auf die Architektur
der Anwendung [ZR01]. Eine solche Migration wäre ein weiterer Ansatzpunkt, der allerdings
schlecht am JCommSy durchgeführt werden kann, da die Datenbank derzeit gleichzeitig von zwei
Systemen verwendet wird.
Eine im Rahmen dieser Arbeit offen gebliebene Frage ist, inwiefern sich die eingesetzten Rah-
menwerke auf die Performance auswirken. Ein möglicher Ansatz wäre, testweise das Hibernate
Rahmenwerk auszutauschen, bzw. SQL-Anfragen in der Service-Schicht zu erzeugen und direkt
auf die Datenbank zuzugreifen. Desweiteren bietet Hibernate die Möglichkeit externe Caches ein-
zubinden. Die Auswirkungen und richtige Konfiguration dieser könnten untersucht werden.
Als Alternative zu den JSPs könnte untersucht werden, wie die Performance sich verändert, wenn
die resultierende HTML-Seite direkt im Servlet in die HTTP-Antwort geschrieben würde. Diese
Untersuchungen sind jedoch nur dann sinnvoll, wenn Performance ein hartes Kriterium ist, weil
sie mit erheblichen Nachteilen für die Architektur verbunden sind.
Web-Anwendungen werden in der Regel von vielen Benutzern gleichzeitig verwendet. Damit
kommt, im Gegensatz zu Desktop-Anwendungen, der Aspekt der Nebenläufigkeit und des Ver-
haltens unter Last hinzu. Beide Aspekte wurden in dieser Arbeit fast vollständig unberücksichtigt
gelassen. Sowohl in Bezug auf die Komplexität der Fragestellung, als auch bezüglich des Auf-
wands, sind sie eine eigene Arbeit wert.
Die durchgeführten Optimierungen haben den Quelltext verändert und dabei teilweise Komplexi-
tät hinzugefügt. Im Rahmen dieser Arbeit konnte nicht detailliert erfasst werden, wie groß diese
Komplexität ist. In weiteren Untersuchungen könnte herausgearbeitet werden, ob der resultierende
Quelltext schwieriger zu lesen und fehleranfälliger ist und inwieweit sich generell die Komplexitä-
ten durch Optimierungen verändern. Dafür wären, neben einer genaueren Betrachtung des Begriffs
Komplexität, empirische Untersuchungen mit mehreren Entwicklern an mehreren Optimierungen
nötig.
Dieser Ausblick verdeutlicht, dass die Gebiete Architektur und Performance weitere offene Fragen
bieten. Die Bedeutung dieser Aspekte wird nicht geringer werden. Die Ergebnisse und Konzepte
dieser Arbeit können Ansatzpunkte für weitere Arbeiten sein.
92 Kapitel 8 Schlussbemerkungen
93
Literaturverzeichnis
[And03] ANDRESEN, Andreas: Komponentenbasierte Softwareentwicklung : mit MDA,
UML und XML. München : Hanser, 2003. – ISBN 3–446–22282–0
[BA04] BECK, Kent ; ANDRES, Cynthia: Extreme Programming Explained : Embrace
Change (2nd Edition). Addison-Wesley Professional, 2004. – ISBN 0–321–
27865–8
[BCK98] BASS, Len ; CLEMENTS, Paul ; KAZMAN, Rick: Software Architecture in Practi-
ce. Addison-Wesley Longman Publishing Co., Inc., 1998. – ISBN 0–201–19930–0
[Ben99] BENTLEY, Jon: Algorithm Alley: Code Tuning in Context. In: Dobb’s Journal of
Software Tools 24 (1999), Mai, Nr. 5, S. 125–126, 128. – ISSN 1044–789X
[BGWA08] BLOCH, Joshua ; GOSLING, James ; WILSON, Steve ; ARNOLD, Ken: Effective
Java (2nd Edition) (The Java Series). 2. Boston, MA, USA : Addison-Wesley
Longman Publishing Co., Inc., 2008. – ISBN 0–321–35668–3
[BH09] BRACHA, Gilad ; HAMILTON, Graham: JSR 294: Improved Modularity Support
in the JavaTM Programming LanguageGilad Bracha. http://jcp.org/en/jsr/
detail?id=294. Version: Januar 2009, Abruf: 15.02.2009
[BHM07] BINDER, Walter ; HULAAS, Jarle ; MORET, Philippe: Advanced Java bytecode
instrumentation. In: PPPJ ’07: Proceedings of the 5th international symposium
on Principles and practice of programming in Java. New York, NY, USA : ACM,
2007. – ISBN 978–1–59593–672–1, S. 135–144
[BHRS07] BEEGER, Robert F. ; HAASE, Arno ; ROOCK, Stefan ; SANITZ, Sebastian: Hi-
bernate: Persistenz in Java-Systemen mit Hibernate 3.2 und dem Java Persistence
API. 2., überarb. und erw. A. dpunkt.verlag, 2007. – ISBN 978–3–89864–447–1
[BMIS04] BALSAMO, Simonetta ; MARCO, Antinisca D. ; INVERARDI, Paola ; SIMEONI,
Marta: Model-Based Performance Prediction in Software Development: A Survey.
In: IEEE Trans. Softw. Eng. 30 (2004), Nr. 5, S. 295–310. – ISSN 0098–5589
[BPKL06] BECKER-PECHAU, Petra ; KARSTENS, Bettina ; LILIENTHAL, Carola: Automati-
sierte Softwareüberprüfung auf der Basis von Architekturregeln. In: BIEL, Bettina
(Hrsg.) ; BOOK, Matthias (Hrsg.) ; GRUHN, Volker (Hrsg.): Software Engineering
Bd. 79, GI, 2006 (LNI). – ISBN 3–88579–173–0, S. 27–37
[BRZ07] BLEEK, Wolf-Gideon (Hrsg.) ; RAASCH, Jörg (Hrsg.) ; ZÜLLIGHOVEN, Heinz
(Hrsg.): Software Engineering 2007, Fachtagung des GI-Fachbereichs Software-
94 Literaturverzeichnis
technik, 27.-30.3.2007 in Hamburg. Bd. 105. GI, 2007 (LNI). – ISBN 978–3–
88579–199–7
[Bul00] BULKA, Dov: Server-Side Programming Techniques. Boston, MA, USA :
Addison-Wesley Longman Publishing Co., Inc., 2000. – ISBN 0–201–70429–3
[CKK02] CLEMENTS, Paul ; KAZMAN, Rick ; KLEIN, Mark: Evaluating Software Archi-
tectures. Addison-Wesley, 2002 (SEI Series in Software Engineering)
[CT05] COMMSY-TEAM: CommSy BenutzerInnenhandbuch Kontext: Hochschule, 2005.
http://www.commsy.net/uploads/Software/commsy_nutzungshandbuch.
pdf, Abruf: 15.02.2009
[Dar09] DARICILI, Ilker: Entwicklung eines J2ME-Clients für eine bestehende Web-
Anwendung - Auswirkungen der Integration auf Architektur, Kontrollfluss und Be-
nutzungsmodell, Universität Hamburg, Diplomarbeit, 2009
[Dmi04] DMITRIEV, M.: Selective profiling of Java applications using dynamic bytecode
instrumentation. In: ISPASS ’04: Proceedings of the 2004 IEEE International
Symposium on Performance Analysis of Systems and Software. Washington, DC,
USA : IEEE Computer Society, 2004. – ISBN 0–7803–8385–0, S. 141–150
[ej-08] EJ-TECHNOLOGIES GMBH: JProfiler Manual, 2008. http://resources.
ej-technologies.com/jprofiler/help/doc/help.pdf, Abruf: 15.02.2009
[FH08] FISCHER, Peter ; HOFER, Peter: Lexikon der Informatik. Springer-Verlag, 2008.
– 966 S.
[Flo07] FLOYD, Christiane: Architekturzentrierte Softwaretechnik. In: [BRZ07], S. 19–24
[Fow99] FOWLER, Martin: Refactoring: Improving the Design of Existing Code. Boston,
MA, USA : Addison-Wesley Professional, 1999. – ISBN 0–201–48567–2
[GHJV95] GAMMA, Erich ; HELM, Richard ; JOHNSON, Ralph ; VLISSIDES, John: De-
sign Patterns: Elements of Reusable Object-Oriented Software. Reading, MA :
Addison-Wesley, 1995
[Gla97] GLASS, Robert L.: Software runaways—some surprising findings. In: SIGMIS
Database 28 (1997), Nr. 3, S. 16–19. – ISSN 0095–0033
[Gro00] GROUP, IEEE Architecture W.: IEEE Std 1471-2000, Recommended practice
for architectural description of software-intensive systems / IEEE. IEEE, 2000. –
Forschungsbericht. – i–23 S.
[Ham97] HAMILTON, Graham: JavaBeans Specification / Sun Microsystems, inc. 1997. –
Forschungsbericht
Literaturverzeichnis 95
[HMRD05] HACKNEY, Ruth ; MOMII, Leigh ; RIGGS, Catrin ; DINGLE, Adair: Profiler tools
selection for curricular support. In: J. Comput. Small Coll. 21 (2005), Nr. 1, S.
177–182. – ISSN 1937–4771
[HN99] HEYDON, Allan ; NAJORK, Marc: Performance limitations of the Java core li-
braries. In: JAVA ’99: Proceedings of the ACM 1999 conference on Java Grande.
New York, NY, USA : ACM, 1999. – ISBN 1–58113–161–5, S. 35–41
[Hoh07] HOHMANN, Christoph: Softwarearchitektur von J2EE Web-Applicationen mit
Components am Beispiel von AJAX, Universität Hamburg, Diplomarbeit, 2007
[hpS] Homepage zum Softwaretomographen. http://www.software-tomography.
de/, Abruf: 15.02.2009
[HQGv02] HARKEMA, M. ; QUARTEL, D. ; GIJSEN, B. M. M. ; VAN DER MEI, R. D.: Per-
formance monitoring of java applications. In: WOSP ’02: Proceedings of the 3rd
international workshop on Software and performance. New York, NY, USA :
ACM, 2002. – ISBN 1–58113–563–7, S. 114–127
[ISO01] ISO: International Standard ISO/IEC 9126, Information technology - Product
Quality - Part1: Quality Model / International Standard Organization. 2001. –
Forschungsbericht
[JB02] JANNECK, Michael ; BLEEK, Wolf-Gideon: Project-based Learning with Comm-
Sy. In: Computer Support for Collaborative Learning: Foundations for a CSCL
Community Bd. CSCL 2002, Stahl, Garry, January 2002, S. 509–510
[Jee05] JEENICKE, Martti: Architecture-Centric Software Migration of Web-based Infor-
mation Systems. In: IRIS28: Proceedings of the 28. Conference on Information
Systems Research in Scandinavia, 2005
[Kau03] KAUPPI, Tarja: Performance analysis at the software architectural level, Univer-
sity of Oulu, Diplomarbeit, 2003
[KBT05] KALIBERA, Tomás ; BULEJ, Lubomír ; TUMA, Petr: Quality Assurance in Per-
formance: Evaluating Mono Benchmark Results. In: [RMS+05], S. 271–288
[KHB06] KOZIOLEK, Heiko ; HAPPE, Jens ; BECKER, Steffen: Parameter Dependent Per-
formance Specifications of Software Components. In: HOFMEISTER, C. (Hrsg.)
et al.: Quality of Software Architectures Bd. 4214, Springer-Verlag, 2006 (LNCS),
S. 163–179
[Knu74] KNUTH, Donald E.: Structured Programming with go to Statements. In: ACM
Comput. Surv. 6 (1974), Nr. 4, S. 261–301. – ISSN 0360–0300
96 Literaturverzeichnis
[Kun06] KUNE, Alexander: Leistungsbewertung von J2EE-Anwendungen - Entwicklung
einer Plattform zur generischen Leistungsbewertung von Anwendungen mit Mehr-
schichtarchitektur, Universität Hamburg, Fachbereich Informatik, Verteilte Syste-
me und Informationssysteme, Diplomarbeit, 4 2006
[Kur02] KURNIAWAN, B.: Java for the Web with Servlet, JSP, and EJB: A Developer’s
Guide to Scalable J2EE Soultions. New Riders Publishing Thousand Oaks, CA,
USA, 2002
[Lea98] LEA, D.: Steps Toward Understanding Performance in Java. In: HCW ’98: Pro-
ceedings of the Seventh Heterogeneous Computing Workshop. Washington, DC,
USA : IEEE Computer Society, 1998. – ISBN 0–8186–8365–1, S. 171
[Lil08] LILIENTHAL, Carola: Komplexität von Softwarearchitekturen, Stile und Strategi-
en, Universität Hamburg, Diss., 2008
[Lin05] LINK, Johannes: Softwaretests mit JUnit. 2. Heidelberg : dpunkt.verlag, 2005. –
ISBN 3–89864–325–5
[Mac82] MACLENNAN, B. J.: Values and objects in programming languages. In: SIGPLAN
Not. 17 (1982), Nr. 12, S. 70–79. – ISSN 0362–1340
[Mey92] MEYER, Bertrand: Applying “Design by Contract”. In: Computer 25 (1992), Nr.
10, S. 40–51. – ISSN 0018–9162
[MM06] MARCO, Antinisca D. ; MIRANDOLA, Raffaela: Model Transformation in Soft-
ware Performance Engineering. In: HOFMEISTER, C. (Hrsg.) et al.: Quality of
Software Architectures Bd. 4214, Springer-Verlag, 2006 (LNCS), S. 95–110
[MyS06] MYSQL AB: MySQL 5.0 Reference Manual, 2006. http://dev.mysql.com/
doc/refman/5.0/en/index.html, Abruf: 15.02.2009
[Par72] PARNAS, D. L.: On the criteria to be used in decomposing systems into modules.
In: Commun. ACM 15 (1972), Nr. 12, S. 1053–1058. – ISSN 0001–0782
[RH06] REUSSNER, Ralf ; HASSELBRING, Wilhelm: Handbuch der Software-Architektur.
1. Aufl. dpunkt.verlag, 2006. – ISBN 3–89864–372–7
[RMS+05] REUSSNER, Ralf (Hrsg.) ; MAYER, Johannes (Hrsg.) ; STAFFORD, Judith A.
(Hrsg.) ; OVERHAGE, Sven (Hrsg.) ; BECKER, Steffen (Hrsg.) ; SCHROEDER,
Patrick J. (Hrsg.): Quality of Software Architectures and Software Quality, First
International Conference on the Quality of Software Architectures, QoSA 2005
and Second International Workshop on Software Quality, SOQUA 2005, Procee-
dings. Bd. 3712. Springer, September 20-22 2005 (Lecture Notes in Computer
Science). – ISBN 3–540–29033–8
Literaturverzeichnis 97
[Sch05] SCHARPING, Arne: Architekturvereinbarungen des JCommSys, Universität Ham-
burg, Diplomarbeit, December 2005. – 28 S.
[Sch07] SCHMALENBACH, Christof: Performancemanagement für serviceorientierte
Java-Anwendungen. Springer-Verlag, 2007 (Xpert-press). – ISBN 978–3–540–
36631–7
[Sch08a] SCHARPING, Arne: Automatisierte Prüfung von Architekturregeln zur Entwick-
lungszeit, Universität Hamburg, Diplomarbeit, 2008
[Sch08b] SCHWENTNER, Henning: Wider die enge Kopplung: Große Refactorings in einem
Prozess zur Entflechtung von Anwendung und Rahmenwerk, Universität Hamburg,
Diplomarbeit, 2008
[Shi00] SHIRAZI, Jack: Java Performance Tuning. O’Reilly, 2000. – ISBN 0–596–00015–
4
[SM02] SIMON, Frank ; MEYERHOFF, Dirk: OO-Metriken zeigen grosse Qualitätspo-
tenziale in komplexen Softwaresystemen. In: Objektspektrum Bd. 6, 2002, S. S.
28–35
[Sun99] SUN MICROSYSTEMS, Inc: White Paper: The Java HotSpot Performance Engine
Architecture. http://java.sun.com/products/hotspot/whitepaper.html.
Version: 1999, Abruf: 15.02.2009
[Sv06] SEEMANN, Jochen ; VON GUDENBERG, Jürgen W.: Software Entwurf mit UML2.
2. Springer-Verlag, 2006 (Xpert.press). – ISBN 3–540–30949–7
[SW00] SMITH, Connie U. ; WILLIAMS, Lloyd G.: Software Performance Antipatterns.
In: Proceedings of the Second International Workshop on Software and Perfor-
mance (WOSP2000), Technology, Inc, 2000, S. 127–136
[SW02] SMITH, Connie U. ; WILLIAMS, Lloyd G.: New Software Performance AntiPat-
terns: More Ways to Shoot Yourself in the Foot. In: 28th International Computer
Measurement Group Conference, Computer Measurement Group, December 2002
(Int. CMG Conference), S. 667–674
[SWLM02] SVAHNBERG, Mikael ; WOHLIN, Claes ; LUNDBERG, Lars ; MATTSSON, Micha-
el: A method for understanding quality attributes in software architecture structu-
res. In: SEKE ’02: Proceedings of the 14th international conference on Software
engineering and knowledge engineering. New York, NY, USA : ACM, 2002. –
ISBN 1–58113–556–4, S. 819–826
[Tat01] TATE, B.A.: Bitter Java. Manning Publications Co. Greenwich, CT, USA, 2001
98 Literaturverzeichnis
[Thi09] THIESEN, Sönke: Verbesserung von Testbarkeit, Entwicklungskomplexität und
Architektur – Austausch der Oberflächentechnologie einer Web-Anwendung, Uni-
versität Hamburg, Diplomarbeit, 2009
[TSL04] TURAU, Volker ; SALECK, Krister ; LENZ, Christopher: Web-basierte Anwen-
dungen entwickeln mit JSP 2: Einsatz JSTL, Struts, JSF, JDBC, JDO, JCA.
dpunkt.verlag, 2004
[Tur00] TURAU, Volker: Java Server Pages Dynamische Generierung von Web-
Dokumenten. Heidelberg : dpunkt.verlag, 2000
[uml05] OBJECT MANAGEMENT GROUP, INC: UML Profile for Schedulability, Perfor-
mance, and Time. 2005 (1.1). – Forschungsbericht
[Ven96] VENNERS, Bill: Inside the Java Virtual Machine. New York, NY, USA : McGraw-
Hill, Inc., 1996. – ISBN 0–07–913248–0
[WK00] WILSON, Steve ; KESSELMAN, Jeff: Java Platform Performance: Strategies and
Tactics. Boston, MA, USA : Addison-Wesley Longman Publishing Co., Inc., 2000.
– ISBN 0–201–70969–4
[WKKRSS01] WERMKE, Matthias (Hrsg.) ; KLOSA, Annette (Hrsg.) ; KUNKEL-RAZUM, Ka-
thrin (Hrsg.) ; SCHOLZE-STUBENRECHT, Werner (Hrsg.): Duden. Bd. Band 5:
Duden. Das Fremdwörterbuch. 7., neu bearbeitete und erweiterte. Mannheim :
Dudenverlag, 2001
[WS02] WILLIAMS, Lloyd G. ; SMITH, Connie U.: PASASM: a method for the perfor-
mance assessment of software architectures. In: Proceedings of the Third Interna-
tional Workshop on Software and Performance (WOSP2002), ACM Press, 2002,
S. 179–188
[Z+98] ZÜLLIGHOVEN, Heinz et al.: Das objektorientierte Konstruktionshandbuch nach
dem Werkzeug & Material-Ansatz. Heidelberg : dpunkt.verlag, 1998. – ISBN
3–932588–05–3
[ZLMG08] ZEITNER, Alfred ; LINNER, Birgit ; MAIER, Martin ; GÖCKELER, Thorsten:
Spring 2.5: Eine pragmatische Einführung. 1. München : Addison-Wesley, 2008.
– ISBN 978–3–8273–2622–5
[ZR00] ZHANG, W.P. ; RITTER, Norbert: Leistungsuntersuchungen hinsichtlich der Nut-
zung von (O)RDBVS als persistente Objektsysteme. In: Grundlagen von Daten-
banken, 2000, S. 111–115
[ZR01] ZHANG, W.P. ; RITTER, Norbert: Leistungsuntersuchung von ORDB-gestützten
objektorientierten Anwendungssystemen. In: HEUER, A. (Hrsg.): Tagungs-
band der GI-Fachtagung ’Datenbanksysteme in Büro, Technik und Wissenschaft’
(BTW’2001), Springer-Verlag, 3 2001, S. 227–243
99
Anhang A
Messergebnisse
Client Client/Asserts Server Server/OptimiertSchicht Zeit [s] Anteil Zeit [s] Anteil Zeit [s] Anteil Zeit [s] Anteilservice 443,2 43,24 % 428,7 40,74 % 377,8 36,90 % 447,3 37,33 %util 37,7 3,68 % 47,8 4,54 % 44,6 4,36 % 63,8 5,33 %presentation 450,8 43,97 % 483,3 45,93 % 512,5 50,06 % 584,3 48,77 %item 81,0 7,90 % 76,9 7,30 % 74,6 7,28 % 83,0 6,98 %domainvalue 7,8 0,76 % 11,5 1,09 % 7,4 0,72 % 5,1 0,43 %migration 2,5 0,25 % 2,5 0,24 % 3,4 0,33 % 13,1 1,09 %logic 2,1 0,21 % 2,7 0,26 % 3,6 0,35 % 1,5 0,12 %Gesamt 1.025 100 % 1.052 100 % 1.024 100 % 1.198 100 %
Tabelle A.1: Vergleich der unterschiedlichen JVM Modi (Stand: 2. Dezember 2008)
100 Anhang A Messergebnisse
PHP WelcomePHP Choose Portal
PHP Login as httpunitPHP
PHP Change roomswitch to announcement index
switch to date indexswitch to group index
open detail announcement directlyswitch to announcement (20 entries)
select next 20 entries announcementselect detail announcement
select detail groupselect announcement detail
next announcement detailannouncement edit
POST announcement edit saveannouncement detail
select group detailjoin a group
leave a groupcreate new announcement
save new announcementdelete created announcement
create new datesave new date
delete created datecreate new group
save new groupdelete created group
display 10 items of group indexdisplay ALL items of group index
search for "TEST"select TestGroup (group detail)
select „save page“expand creator info
sort by "bearbeitet von"sort by "bearbeitet von" rev
sort by "Titel"sort by "bearbeitet"
PHP logout
0 500 1000 1500 2000 2500 3000 3500
144
605
1074
747
2976
717
529
414
256
564
731
259
514
267
256
472
123
264
220
236
243
161
117
652
170
116
623
158
402
531
285
488
308
227
688
220
801
816
812
789
1155
Zeit in Millisekunden
An
fra
ge
Abbildung A.1: Durchschnittliche Antwortzeiten des JCommSy ohne Profiler (Stand: Okt. 2008)
101
Abbildung A.2: Gegenüberstellung der minimalen Antwortzeiten für einen Wechsel der Über-sichtsseiten mit und ohne Ajax.Der rot markierte Bereich hebt den eigentlichen Vergleich hervor.
Abbildung A.3: Gegenüberstellung der durchschnittlichen Antwortzeiten für einen Wechsel derÜbersichtsseiten mit und ohne Ajax.Der rot markierte Bereich hebt den eigentlichen Vergleich hervor.
102 Anhang A Messergebnisse
103
Anhang B
Testfälle
Im Folgenden wird zu jedem Testfall des Benchmarks (siehe 5.2.3) die genaue Abfolge der Ak-
tionen aufgeführt.
Übersichtsseiten : Ankündigungen→ Termine→ Gruppen→ Gruppen→ Termine→ Ankün-
digungen→ Gruppen→ Ankündigungen
Lesen : direkt von der Übersichtsseite auf eine Ankündigung (neu) → Ankündigungsübersicht
→ nächsten 20 Einträge→Ankündigung auswählen→ Gruppen→ Gruppen auswählen→Gruppen
Bearbeiten : Ankündigungen → Ankündigung auswählen → nächste Ankündigung auswählen
→ Text bearbeiten→Gruppenübersicht→Gruppe auswählen→Gruppe beitreten→Grup-
pe austreten→ Übersicht
Erstellen/Löschen : Ankündigungen → erstellen → speichern → löschen → Termine erstellen
→ speichern→ löschen→ Gruppen erstellen→ speichern→ löschen→ Gruppenübersicht
Spezialfunktionen : Gruppen→ 10 Einträge anzeigen→ alle Einträge anzeigen→ suche nach
„TEST“ → wähle DummyTestGroup → Seite speichern → „zuletzt bearbeitet von“ auf-
klappen→ Übersicht
Sortieren : Ankündigungen→ sortieren nach „bearbeitet von“→ sortieren nach „bearbeitet von“
(Absteigend) → sortieren nach „bearbeitet von“ → sortieren nach Titel → sortieren nach
bearbeitet
104 Anhang B Testfälle
Eidesstattliche Erklärung
Ich versichere, dass ich die vorstehende Arbeit selbstständig und ohne fremde Hilfe angefertigt
und mich anderer als der im beigefügten Verzeichnis angegebenen Hilfsmittel nicht bedient habe.
Alle Stellen, die wörtlich oder sinngemäß aus Veröffentlichungen entnommen wurden, sind als
solche kenntlich gemacht.
Ich bin mit einer Einstellung in den Bestand der Bibliothek des Departments Informatik einver-
standen.
Hamburg, den Unterschrift: