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()
oderRuntime.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.
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. –
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. –
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. –