Verteilte und Parallele Programmierung

PD Stefan Bosse
Universität Bremen, FB Mathematik & Informatik
SS 2020
Version 2020-05-12

Einführung in die verteilte und parallele Programmierung

Programmierung

Programmiersprachen - Sequenzielle

  • Assembler Maximale Performanz, minimalste Zuverlässigkeit/Korrektheit (Fehler), minimalster Speicherbedarf, kein automatisches Speichermanagement

  • C/C++ Sehr gute Performanz, geringer Speicherbedarf, und mittlere Zuverlässigkeit/Korrektheit (Fehler), kein oder minimales automatisches Speichermanagement Prozedurale Programmierung

  • JAVA Mittlere Performanz, hoher Speicherbedarf, gute Zuverlässigkeit/Korrekheit (Fehler), automatisches Speichermanagement Objektorientierte Programmierung

  • Skriptsprachen Unterschiedliche Performanz, wird durch Interpreter bestimmt, automatisches Speichermanagement

    • Python Bytecode (schlechte Performanz)
    • JavaScript Bytecode / JIT (mittlere oder gute Performanz, evtl. hoher Speicherbedarf)
    • Lua Bytecode / JIT (mittlere oder gute Performanz, niedriger Speicherbedarf)

Programmierung

Programmiersprachen - Parallele

Communicating Sequential Processes (CSP)
  • OCCAM und OCCAM-π Mehrprozessmodell nach CSP/Hoare und Pi-Calculus (1980/2000)

  • Limbo (Inferno OS)

  • Go

  • Datenpfadparallelität: HP Fortran, Fortran M

  • Funktionale Programmiersprachen (Haskell, Erlang, OCaML, JoCAML) bieten inhärente Parallelität auf Datenpfadebene

  • Lua. Ach doch? Unterstützt wenigstens das Konzept der Prozessblockierung

  • JCSP : Java + CSP

Programmierung

  • Reine Bytecode Interpreter haben geringe oder mittlere Performanz bei niedrigen Speicherbedarf
    • Der Bytecode wird entweder vollständig aus dem Quelltext beim Start eines Programms erzeugt, oder
    • Der Bytecode wird stückweise nach Bedarf aus dem Quelltext zur Laufzeit erzeugt

Just-in-time Compiler

  • Ein JIT Compiler übersetzt häufig vorkommende Bytecode Abschnitte zur Ausführungszeit in nativen Maschinencode

    • Das ergibt erhöhte Ausführungsperformanz, benötigt aber mehr Speicher
  • Vorteile von Skriptsprachen gegenüber kompilierten Programmen: Schneller Test, ausführliche und genaue Rückmeldung vom Interpreter bei Fehlern, bessere Laufzeitüberwachung von Fehlern,

Virtualisierung

  • Motivation von Virtualisierung:
    • Abstraktion der Maschine und Speicher
    • Abstraktion und einfache Wiederverwendung von Algorithmen
    • Abstraktion von Kommunikation
    • Abstraktion von Parallelisierung und Verteilung!

figvm1


Abb. 1. Eine prozessvirtuelle Maschine, die Programme ausführen kann, die für ein anderes Betriebssystem und eine andere ISA entwickelt wurden: Die Virtualisierungssoftware bildet eine Plattform auf eine andere ab, und übersetzt eine Reihe von Betriebssystem- und Benutzerebenenbefehlen. [A]

Virtualisierung

Compiler

figcc[http://doc.gold.ac.uk]


Abb. 2. Klassischer Softwareentwurf mit Compiler und Linker (C)

Virtualisierung

Interpeter

figinterpreter


Abb. 3. Edit - Compile - Execute Zyklus bei einem Interpreter

Virtualisierung

Virtuelle Maschine und Bytecode Interpreter

  • Die Übersetzung des Quelltextes in Bytecode kann vor und während der Ausführung des Programms erfolgen!

  • Ausführung des Bytecodes durch VM. Vorteile:

    • Unabhängig von Hostplattform
    • Virtuelle Maschine kann optimiert werden ohne dass der Bytecode neu erzeugt werden muss
    • Abstraktion der Speicherverwaltung (automatisches Speichermanagement)
    • Parallelisierung kann durch VM automatisch erfolgen (wenn möglich)
    • Abstraktion der IO Schnittstellen und Kommunikation
    • VM kann Bytecode zu Beginn der Ausführung oder zur Laufzeit dynamisch und nach Bedarf optimieren (JIT)

Virtualisierung

figbytecode1[Python]


Abb. 4. Vergleich Interpreter mit Bytcode Compiler-Interpreter System

Virtualisierung

Serialisierung

  • Da Bytecode unabhängig von der Hostplattform sein sollte, kann Bytecode einfach von einer Maschine zu einer anderen übertragen und ausgeführt werden
  • Dazu ist eine Serialisierung von Daten und Code erforderlich (Flache Liste von Bytes), mit anschließender Deserialisierung (Wiederherstellung der Daten- und Codestruktur)

figbytecode2[Peter Cawley]

Virtualisierung

JIT Compiler

  • Neben der Bytecode Ausführung kann während der Laufzeit des Programms der Bytecode stückweise optimiert werden Erzeugung von Maschinencode durch einen Just-in-Time Compiler (JIT)

figjit1[King Fahd University of Petroleum and Minerals]


Abb. 5. Vergleich Interpreter mit JIT Compiler-Interpreter Systeme

Virtualisierung

Aufgabe

  1. Überlege Dir die Vor- und Nachteile von

    • Compilern
    • Interpretern
    • Bytecode Interpretern
    • JIT Compiler und Interpreter
  2. Was sind die Bewertungskriterien?

  3. Welche Ausführungsarchitekturen könnten für Parallele Systeme mit VMs geeignet sein?

  4. Wo gibt es Engpässe?


Virtualisierung

  • Rechenleistung, Speicherbedarf, und Ein-Ausgabe Durchsatz/Latenz sind wichtige Metriken für den Einsatz in Eingebetteten Systemen

figembarch1[B]


Abb. 6. Typische Datenverarbeitungsebenen in Eingebetteten Systemen und Virtualisierung

Virtualisierung

Perfomanz von Interpretern

Dhrystone Benchmark (Kombination aus Berechnung, Objekten, Arrays, Strings, Funktionen)

VM/OS-ARCH Linux-i686 Linux-armv6l (PI-3) Linux-armv6l (PI Zero)
python2.71 105k/s 10k/s 4k/s
lua 5.12 140k/s - -
luajit 2.0.5X2 660k/s 71k/s 40k/s
jerryscript 1.1.7X3 45k/s - -
nodejs/V8-43 6300k/s 350k/s 40k/s
C/gcc 18000k/s ? ?

(1: Python, 2: Lua, 3: JavaScript)

Virtualisierung

Speicherbedarf von Interpretern

Dhrystone Benchmark (Kombination aus Berechnung, Objekten, Arrays, Strings, Funktionen)

VM/OS-ARCH Linux-i686 Linux-armv6l (PI-3) Linux-armv6l (PI Zero)
python2.7 - - -
lua 5.1 - - -
luajit 2.0.5X 2MB - -
jerryscript 1.1.7X 2MB
nodejs 4 24MB - -
C/gcc 1MB ? ?

Virtualisierung

Automatisches Speichermanagement

  • In C/C++ muss für jedes zur Laufzeit dynamisch erzeugte Datenobjekt (Array, String, Record, ..) immer explizit Speicherplatz im Hauptspeicher angefragt werden (malloc,new) und wieder frei gegeben werden wenn das Datenobjket nicht mehr benötigt wird (free,delete)

  • In Skriptsprachen gibt es i.A. ein automatisches Speichermanagement mit einem sog. Garbage Collector und Objektreferenzierung

  • Jedes Datenobjekt welches verwendet wird (z.B. in Variablen oder Funktionen) besitzt eine Referenz

  • Es muss eine Wurzeltabelle geben von der aus alle Referenzen auf verwendete Objekte auffindbar sind

Virtualisierung

  • Objekte: In C/C++/JAVA usw: Variablen, in Lua/JavaScript/OCaML: Werte!

Bsp. 1. (Beispiel von Objektreferenzierungen in Lua: Wo existieren Referenzen?)

\[\begin{mdmathpre}%mdk
\mathkw{local}~\mathid{o}_{1}~=~\{~\mathid{a}=1,~\mathid{b}=\{1,2,3,4\}~\}\\
\mathkw{local}~\mathid{o}_{2}~=~\{~\mathid{op}~=~\mathid{o}_{1},~\mathid{index}=2~\}\\
\mathkw{function}~\mathid{show}~(\mathid{o})~\mathid{print}~(\mathid{o}_{2}.\mathid{op}.\mathid{a}-\mathid{o}.\mathid{a}~\}~\mathkw{end};~\mathid{show}(\mathid{o}_{1})
\end{mdmathpre}%mdk
\]

Virtualisierung

figgc1


Abb. 7. Verschiedene Objekte: Objekte die nicht mehr referenziert sind, werden freigegeben

Virtualisierung

Speicherarchitektur von Programmen

  • Heap: Datenobjekte mit längerer Lebensdauer (Tabellen- oder Listenstruktur) Benötigt Speichermanagement
  • Stack: Datenobjekte mit kurzer Lebensdauer (Stapelstruktur) Benötigt kein Speichermanagement

figprogmem1


Abb. 8. Speicherhierarchie von Programmen und Zusammenhang mit Maschinenanweisungen

Virtualisierung

Speicherorganisation von Virtuellen Maschinen

figvmmem1

Virtualisierung

Speicher

  • Die meisten Objekte in Programmiersprachen benötigen Speicher

    • Lokale, temporäre, und globale Variablen
    • Objekte
    • Arrays
    • Zeichenketten
    • Funktionen (!)
  • Der Kontext von Speicherobjekten ist wichtig:

    • Modul limitierte Programmsichtbarkeit
    • Lokal stark limitierte Programmsichtbarkeit
    • Global uneingeschränkte Sichtbarkeit

Virtualisierung

  • Beispiele: Die globale Variable Global ist überall, auch außerhalb des Definitionsmoduls, sichtbar, die Variable lokal ist im gesamtem Modul und den Funktionen f1 und f2 sichtbar, wo hingegen lokal1 und lokal2 jeweils nur ihrem Funktionskontext sichtbar sind.
Global = 1
local lokal = 2
function f1 () 
  local lokal1 = 3
end
function f2 () 
  local lokal2 = 3
end
  • Nur wenn Variablen bei der Ausführung sichtbar sind belegen sie Speicher!

Virtualisierung

  • Ein Teil des Speicher ist vorbelegt:

    • Globale Variablen
    • Funktionen, ausführbarer Code,
  • Ein anderer Teil des Speichers wird zur Laufzeit auf dem Stack und Heap belegt und benötigt Verwaltung:

    • Lokale und temporäre Variablen, ..
    • Funktionen, ausführbarer Code,
  • Achtung: Bei Programmiersprachen bei denen Funktionen Werte erster Ordnung sind befindet sich auch Code im Heap oder auf dem Stack!

Virtualisierung

Speicherverwaltung

Listen

Freie und belegte Speicherbereiche werden durch Listen (einfach- oder doppelt verkettet) verwaltet

Tabellen

Tabellen (Hashtabellen) werden zur Speicherverwaltung verwendet

Manuelle Verwaltung

Speicherbelegung explizit vom Programmierer und teils vom Compiler auszuführen

Vorteile

  • Optimale Speicherbelegung (tatsächlich naiv und falsch) und Performanz/Effizienz

Nachteile

  • Speicherlecks (nicht mehr benötigter Speicher wird nicht frei gegeben)
  • Mehrfachfreigabe (Inkonsistenz der Speicherverwaltung)
  • Benutzung von Speicherobjekten nach Freigabe

Virtualisierung

Automatische Verwaltung
Die Freigabe von Speicherbereichen wird automatisch durch einen Garbage Collector durchgeführt. Dieser bestimmt auch automatisch ob Speicherobjekte noch benötigt werden.

Vorteile

  • Speicherlecks werden vermieden
  • Keine Mehrfachfreigabe (Inkonsistenz der Speicherverwaltung)
  • Keine Benutzung von Speicherobjekten nach Freigabe

Nachteile

  • Nicht optimale Speicherbelegung und schlechtere Performanz/Effizienz

Garbage Collection

  • Wie soll der GC heraus finden ob ein Objekt noch benötigt wird?
    1. Referenzzähler wird jedem Objekt hinzugefügt und bei jeder neuen Referenzierung erhöht (Allocate). Ein Objekt mit Referenzwert Null kann frei gegeben werden.

Virtualisierung

  1. Der GC rechnet Objektabhängkeiten aus und markiert Objekte die freigegeben werden können
  • Beispiel von Referenzen und Sichtbarkeit (Programmkontext)
\[\begin{mdmathpre}%mdk
\mathkw{local}~\mathid{o}_{1}~=~\{~\mathid{x}=100,\mathid{y}=200~\}^{\mathid{e}_{1}}~~~~~~~~~~~~~~~[\mathid{e}_{1}.\mathid{ref}=1]\\
\mathkw{local}~\mathid{o}_{2}~=~\{~\mathid{pos}=\mathid{o}_{1},~\mathid{color}="\mathid{red}"~\}^{\mathid{e}_{2}}~~[\mathid{e}_{1}.\mathid{ref}=2]\\
\mathkw{function}~\mathid{draw}~(\mathid{o})\\
\mdmathindent{2}\mathkw{local}~\mathid{o}_{3}~=~\{~\mathid{pos}=\mathid{o}.\mathid{pos},~\mathid{color}="\mathid{white}"~\}^{\mathid{e}_{3}}~~[\mathid{e}_{3}.\mathid{ref}=1,~\mathid{e}_{1}.\mathid{ref}=4]\\
\mdmathindent{2}\mathkw{local}~\mathid{o}_{4}~=~\{~\mathid{pos}=\{\mathid{x}=\mathid{o}.\mathid{pos}.\mathid{x},\mathid{y}=\mathid{o}.\mathid{pos}.\mathid{y}\}^{\mathid{e}_{4}},~\mathid{color}="\mathid{black}"~\}~[\mathid{e}_{1}.\mathid{ref}=4(5?)!]\\
\mdmathindent{2}\mathid{draw}(\mathid{o}_{3})\\
\mdmathindent{2}\mathid{draw}(\mathid{o}_{4})\\
\mdmathindent{2}\rightarrow [\mathid{e}_{3}.\mathid{ref}=0,~\mathid{e}_{4}.\mathid{ref}=0,~\mathid{e}_{1}.\mathid{ref}=3,~\mathid{e}_{2}.\mathid{ref}=2]\\
\mathkw{end}\\
\mathid{draw}(\mathid{o}_{2})~~[\mathid{e}_{2}.\mathid{ref}=2,~\mathid{e}_{1}.\mathid{ref}=3]\\
\mathid{o}_{2}=\mathid{nil}~~~~[\mathid{e}_{2}.\mathid{ref}=0,~\mathid{e}_{1}.\mathid{ref}=1]\\
\mathid{o}_{1}=\mathid{nil}~~~~[\mathid{e}_{1}.\mathid{ref}=0]
\end{mdmathpre}%mdk
\]
  • Wichtig: Referenzzähler beziehen sich auf Werte (ei), nicht auf die Variablen selber (die nur Zeiger auf Werte besitzen)
    • D.h. wenn ein Wert als Referenz an eine weitere Variable zugewiesen wird erhöht sich der Referenzzähler!

Virtualisierung

GC: Referenzzählung

  • Wenn ein Objekt (also ein Wert) erzeugt wird, wird der Referenzzähler auf Null gesetzt

  • Jede weitere Referenzierung erhöht den Zähler um eins (also bereits das zuweisen eines Objektes an eine variable!)

  • Wird eine Referenzierung vom Objekt entfernt (explizit =null, oder implizit durch Entfernen des referenzierenden Objektes), dann wird der Zähler um eins erniedrigt

  • Ist der Zähler wieder Null, dann wird der Speicher freigegeben

Nachteile

  1. Zyklische Referenzen können nicht aufgelöst werden:
local o1 = { x=100,y=100 }
local o2 = { prev=o1 }
o1.next = o2

Virtualisierung

  1. Daher werden u.U. nicht alle Objekte freigegeben Speicherlecks
  2. Der Referenzzähler belegt selber Speicher (gehört zum Objekt). Bei low-resource Plattformen wird z.B. eine Datenwortbreite von 16 Bit für den Zähler verwendet, und der maximale Referenzwert ist 65536 (jerryscript ist so ein Kandidat)!
  3. Allokation ist langsamer (verlangsamt Zuweisung)

Vorteile

  1. Einfach zu implementieren
  2. Freigabe ist effizient (wenig Rechenaufwand)

GC: Mark & Sweep

  • Bestimmte Objekte sind direkt zugänglich, und es muss herausgefunden werden welche Objekte erreichbar sind Graphensuche

  • Es gibt einen Stammsatz von Speicherplätzen im Programm, dessen Objekte direkt erreichbar sind.

Virtualisierung

Mark-and-Sweep verläuft in zwei Phasen

Markierungsphase

  • Erreichbare Objekte suchen:
    • Der Stammsatz wird zu einer Arbeitsliste hinzugefügt
    • Solange die Arbeitsliste nicht leer ist wird ein Objekt von der Liste entfernt. Wenn es nicht markiert ist, markiere es als erreichbar und alle Objekte die davon aus erreichbar sind werden zur Arbeitsliste hinzugefügt.

Sweepphase

  • Für alle belegten Objekte:
    • Ist das Objekt nicht markiert so lösche es (Speicher freigeben)
    • Ist das Objekt markiert, lösche die Markierung

Virtualisierung

Vorteile

  1. Auch zyklische Referenzen können über die Arbeitsliste aufgelöst werden
  2. Alle Objekte können frei gegeben werden
  3. Allokation schnell

Nachteile

  1. Rechenintensiv und langsam!!

figgcms3


Abb. 9. Beispiel eines M&S Durchlaufes mit anschließender Freigabe von nicht mehr erreichbaren == benötigten Objekten

Multitasking

  • Auf einem Rechnerknoten gibt es eine Vielzahl von Aufgaben die bearbeitet werden müssen:

    • Datenverarbeitung
    • Kommunikation
    • Speicherung und Ein/Ausgabe
  • Zum Teil müssten Aufgaben parallel verarbeitet werden Multitasking Parallelisierung nur bedingt möglich Task Scheduling erforderlich

  • Es gibt verschiedene Ausführungsebenen für Tasks:

    1. Prozesse
    2. Threads
    3. Fibers/Koroutinen

Virtualisierung und Parallelisierung

Grundkonzept: Aufteilung einer Berechnung in eine Menge von Teilberechnungen die auf verschiedenen Prozessoren parallel ausgeführt werden.

  • Auf generischen Mehrprozessorsystemen (Multicore) wird Multithreading verwendet Mehrere Kontrollpfade
  • Auf spezialisierten Mehrprozessorsystemen (GPU) wird sowohl Mutlithreading als auch Datenpfadparallelität ausgenutzt (Mehrere Kontroll- und Datenpfade)

Aber: Bei Virtuellen Maschinen lassen sich diese Konzepte nicht direkt übertragen!

  • Automatisches Speichermanagement (“stop the world” Phase) schwierig zur parallelisieren (Konkurrenz und Ressourcenkonflikte)
  • Speicherraum einer VM ist gekapselt (man spricht von einer Ausführungsinstanz)
  • VM arbeiten daher i.A. mit nur einem einzigen Kontrollfluss!

Virtualisierung und Parallelisierung

Architekturen Paralleler VMs

  1. Background/Foreground Systeme
    • Es gibt einem Hauptthread der das Programm ausführt (VM Main Loop)
    • Es gibt einen Nebenthread der die GC ausführt
  2. Multithreading
    • Es gibt einen oder mehrere Threads die das Programm ausführen
    • Es gibt einen oder mehrere Threads die den GC ausführen

Virtualisierung und Parallelisierung

img-#figure-gcpar01[Jones, 2012, TGCH]


Abb. 10. (a) Sequenzielles System (1 Prozess/Thread) Stop-the-world GC (b) Multithreading Stop-the-world GC (c) Multithreading und paralleler/nebenläufiger GC

Virtualisierung und Parallelisierung

Parallelisierung des GC Algorithmus

  1. Parallele Markierung

  2. Paralleles Kopieren

  3. Paralleles Sweeping

  4. Paralleles Kompaktieren

  • Es wird weiterhin Synchronisation zw. den parallel ausgeführten GC Threads und dem eigentlichen VM Ausführungsthread (oder mehreren) benötigt!

  • Es gibt zwei Motivationen:

    • GC soll möglich wenig Rechenzeit des Hauptprogramms “kosten”
    • GC soll beschleunigt werden

Virtualisierung und Parallelisierung

Synchronisation

  • Neben der Synchronisierung von Operationen an Kollektordatenstrukturen kann es auch erforderlich sein, Operationen auf einzelnen Objekten zu synchronisieren.

  • Das Markieren ist im Prinzip eine idempotente Operation: Es spielt keine Rolle, ob ein Objekt mehr als einmal markiert wird.

  • Wenn z.B. ein Kollektor jedoch einen Vektor von Markierungsbits verwendet, muss der Marker diese Bits atomar setzen

    • Befehlssätze moderner Prozessoren bieten nicht die Möglichkeit, ein einzelnes Bit in einem Wort oder Byte zu setzen Setzen einer Markierung erfordert eine Schleife, die versucht, den Wert des gesamten Bytes atomar zu setzen.
    • Wenn das Markierungsbit im Header des Objekts enthalten ist oder der Markierungsvektor ein Vektor von Bytes ist (einer pro Objekt), ist keine Synchronisation erforderlich

Virtualisierung und Parallelisierung

Also: Die Details von Rechnerarchitektur und GC Algorithmus bestimmen notwendige Synchronisation

  • Zum GC Algorithmus gehören auch immer die Datenstrukturen und Allokationsmethoden!

  • Schließlich muss die Beendigung einer Sammelphase korrekt bestimmt werden!

    • Die Verwendung paralleler Threads macht die Terminierungserkennung deutlich komplexer.
    • Grundsätzlich besteht das Problem darin, dass ein Thread möglicherweise versucht zu erkennen, ob die Phase beendet wurde, während ein anderer noch arbeitet und eine neue Phase startet!
  • Barrieren können z.B. für die Synchronisation bei der konkurrierenden/parallelen Kollektion eingesetzt werden

Virtualisierung und Parallelisierung

Parallele vs. Konkurrierend

  • Und wieder muss zwischen paralleler und konkurrierender Ausführung unterschieden werden, was zu unterschiedlichen Abläufen des GC und der VM Main Loop führen

img-#figure-gcpar02[Jones, 2012, TGCH]


Abb. 11. (rot) Parallele und synchronisierte Ausführung (VM Main Loop angehalten) (blau) Konkurrierende Ausführung und überlappend mit VM Main Loop

Virtualisierung und Parallelisierung

Parallele VM Instanzen

  • Parallelität daher durch mehrere VM Instanzen möglich.
    • Eine Instanz Ein Prozess (Thread)
    • Aber: Die Start- und Initialisierungszeit einer VM Instanz kann hoch sein (JS nodejs/v8: 100ms, LuaJit: 1ms)
    • Weiterhin ist der Speicherbedarf einer VM Instanz zu beachten (“baseline” ohne Nutzprogramm, nodejs: 20MB, LuaJit: 1MB)

Echtzeitverarbeitung

  • Echtzeitverarbeitung bedeutet die Ausführung eines Tasks innerhalb eines vorgegebenen Zeitintervalls [t0,t1]
    • Soft Realtime: Zeitüberschreitung bei weicher Echtzeitanforderung Tolerierbar
    • Hard Realtime: Zeitüberschreitung bei harter Echtzeitanforderung Systemfehler

figrealtimespec1


Abb. 12. Echtzeitspektrum: Weiche und harte Echtzeitanforderungen bei unterschiedlichen Applikationen [B]

Echtzeitverarbeitung

figtaskarrivel1


Abb. 13. Periodische, sporadische, und aperiodische Tasks [B]

Echtzeitverarbeitung

Preemption

  • Wenn schon nicht alles Tasks gleichzeitig verarbeitet werden können, dann wenigstens die wichtigsten vorrangig ausführen

  • Das erfordert aber:

    • Festlegung einer Priorität
    • Bei Echtzeitanforderungen die Unterbrechung von unterlegenen (kleiner Priorität) Tasks

figpreempt1


Abb. 14. Unterschied preemptive und nicht preemptive Ausführung von Tasks [B]

Echtzeitverarbeitung

Virtuelle Maschinen

  • Schon die Parallelisierung der Programmausführung ist schwierig und eher schwergewichtig!

  • Dis Ausführungszeiten von Programmen sind nicht im voraus bekannt und deterministisch

    • JIT beeinflusst die Ausführungszeit zur Laufzeit signifikant (Übersetzung kostet Zeit, Maschinenkode spart Zeit)
    • Automatisches Speichermanagement (GC) führt zu teils stark variierenden Laufzeiten und Performanz von Programmen
    • Bei längerer Laufzeit kann sich die Performanz (Latenz/Datendurchsatz) verringern. Warum?

Echtzeitfähigkeit durch automatisches Speichermanagement (“stop the world” Phase), JIT, und umfangreiche Nutzung dynamischer Datenstrukturen kaum zu gewährleisten!