2016-10-26 5 views
3

Wir betreiben ein RT-System in Java. Es verwendet häufig relativ große Heaps (100 + GB) und bedient Anfragen aus der Nachrichtenwarteschlange. Jede Anforderung muss schnell bearbeitet werden (< 100 ms), um die SLAs zu erfüllen.Steuerung der Java-Speicherbereinigung im Echtzeitsystem

Es treten schwerwiegende GC-Probleme auf, da es häufig dazu kommt, dass GC während einer Anforderung die Stop-the-World-Sammlung verursacht (200 + ms), was zu einem Fehler führt.

Einer unserer Entwickler mit angemessenen Kenntnissen von GCs verbrachte einige Zeit damit, GC-Parameter zu optimieren und verschiedene GCs auszuprobieren. Nach einigen Tagen kam er zu einer Parametrisierung, die wir scherzhaft "durch genetischen Algorithmus entwickelt" nennen. Es senkt die GC-Pausen, ist aber immer noch weit davon entfernt, die SLA-Anforderungen zu erfüllen.

Die Lösung ich suche ist einige kritische Teile des Codes schützen von GC, und nach einer Anfrage fertig ist, lassen Sie die GC zu tun, so viel Arbeit, wie es braucht, vor nächste Anforderung nehmen. Gelegentliche Pausen außerhalb der Anfragen wären in Ordnung, weil wir mehrere Arbeiter haben und Müllsammler nur eine Zeitlang keine Anfragen stellen würden.

Ich habe einige Ideen, die dumm, hässlich sind und die meisten wahrscheinlich nicht funktioniert, aber hoffentlich zeigen sie das Problem:

  • Gelegentlich Thread.sleep() im Aufnahmegewinde nennen, für die GC beten einige Arbeit zu tun in inzwischen
  • Invoke System.gc() oder Runtime.gc() zwischen Anfragen, wieder beten hoffnungslos für sie,
  • Mess wie https://stackoverflow.com/a/6915221/1137187 den Code vollständig mit hacky Muster zu helfen.

Die letzte wichtige Anmerkung ist, dass wir ein Low-Budget-Start und kommerzielle Lösungen wie Zing® sind keine Option für uns, wir sind für eine nicht-kommerzielle Lösung suchen.

Irgendwelche Ideen? Wir würden unseren Code vollständig in C++ umschreiben (wir wussten nicht, dass GC am Anfang ein Problem und nicht Lösung sein könnte), aber die Code-Basis ist schon zu groß, um das zu tun.

+0

Java ist sicherlich nicht die erste Sprache, die mir vorkommt, wenn ich den Begriff "Echtzeit" höre, und angesichts der Tatsache, dass Java ausgewählt wurde, scheint die Notwendigkeit eines gigantischen Heaps nicht gut zu sein. –

+2

In jedem Fall gibt es wirklich nur zwei allgemeine Ansätze zu einem GC-Problem in einem lang laufenden Prozess: (1) Reduzieren Sie die Menge an Müll produziert, und (2) den Müll schneller sammeln zu machen. Wenn vollständige GCs teuer, aber selten sind, kann eine Alternative die Reduzierung der Heap-Größe sein. Das erfordert häufigere GCs, aber jeder sollte schneller sein, da nicht so viel Müll gesammelt werden kann. Versuchen Sie außerdem, langlebige Objekte zu vermeiden, die für eine generationelle GC teurer sind, wenn sie nicht für die gesamte Lebensdauer des Prozesses beibehalten werden. –

+2

Außerdem vorsichtig mit temporären Objekten. Es ist nicht ungewöhnlich, dass Java-Programmierer sich viel mehr auf GC verlassen, als durch das Erstellen und Verwerfen von vielen Objekten. Sie erkennen vielleicht nicht einmal, dass sie es tun. String-Verkettung und Autoboxing können hier zum Beispiel beitragen. Primitive haben keine GC-Kosten und als Faustregel erzeugen niedrigere APIs weniger Müll. –

Antwort

1

Irgendwelche Ideen?

Verwenden Sie eine andere JVM? Azul behauptet, mit solchen Fällen umgehen zu können. Redhat trägt Shenandoah zu openjdk mit ähnlichen Zielen bei, also könntest du vielleicht experimentelle Builds ausprobieren, wenn du keine kommerzielle Lösung willst.

Es gibt auch andere JVMs, die sich auf Echtzeitanwendungen konzentrieren, aber wie ich es verstehe, konzentrieren sie sich auf härtere Echtzeitanforderungen auf kleineren Systemen, deins hört sich eher nach weichen Echtzeitanforderungen an.

Eine andere Sache, die Sie versuchen können, ist die signifikante Reduzierung von Objektzuweisungen (Profil Ihrer Anwendung!) Durch Verwendung von zuvor zugewiesenen Objekten oder kompakteren Datenrepräsentationen, wo anwendbar. Die Verringerung des Allokationsdrucks bei gleichzeitiger Beibehaltung der neuen Gengröße bedeutet eine erhöhte Sterblichkeitsrate pro Sammlung, die junge Sammlungen beschleunigen dürfte.

Die Auswahl der Hardware zur Maximierung der Speicherbandbreite könnte ebenfalls hilfreich sein.

Invoke System.gc() oder Runtime.gc() zwischen den Anforderungen, wieder beten hoffnungslos für sie zu helfen,

Diese Macht Arbeit, wenn sie mit -XX:+ExplicitGCInvokesConcurrent kombiniert, sonst wäre es ein auslösen single-threaded STW-Sammlung mit CMS oder G1 (ich nehme an, dass Sie eine davon verwenden). Aber dieser Ansatz scheint brüchig zu sein und erfordert viel Abstimmung und Überwachung.

+0

Ich gab einige Versuche, die 'System.gc()' + '-XX: + ExplicitGCInvokesConcurrent', aber es funktioniert tatsächlich nicht, hauptsächlich weil' System.gc() 'läuft für einige Minuten. Das Ausführen eines vollständigen GC scheint keine Option zu sein. Und ja, wir haben sowohl das CMS als auch G1 ausprobiert. Will alternative JVMs und Profiling + Refactoring versuchen, wenn nichts anderes hilft. Je mehr darüber nachgedacht wird, desto mehr bin ich davon überzeugt, dass es für mich genau das ist, was ich der JVM sage, wenn ich die Sammlung mache. Tuning GC-Parameter können mein Problem prinzipiell nicht lösen, ohne JVM zusätzliche Informationen zu geben. – Tregoreg

+0

Nun, Sie sagen Pausen sind 200ms und Ihre SLA ist <100ms. So scheint es in Reichweite zu sein, wenn Sie einen Faktor von 2-3 drücken können. – the8472

Verwandte Themen