JDK 27 introduceert ongeveer 350 wijzigingen in de stop-the-world collectors van de HotSpot VM. De meest significante update is JEP 523, waardoor G1 nu de standaard garbage collector is voor alle omgevingen, ter vervanging van Serial GC.
Belangrijke technische wijzigingen per component:
- Algemeen: Introductie van
Atomic<T> voor thread-shared variabelen en optimalisatie van TLAB-dimensionering om verspilling bij kortstondige threads te beperken.
- G1 GC: Aanpassingen in heap-dimensionering (standaardwaarden van
MinHeapFreeRatio en MaxHeapFreeRatio gewijzigd naar 0% en 100%), verbeterde concurrent marking en fixes voor het onbedoeld in leven houden van humongous objects.
- Parallel GC: Verbetering van de adaptive tenuring threshold, waardoor objecten efficiënter worden gepromoveerd, en correcties in heap-uitbreiding bij grote allocaties.
- Overig: Verbeterde logging en JFR-events voor string deduplicatie.
JDK 27 wijzigingen in G1/Parallel/Serial GC
In totaal zijn er ongeveer 350 wijzigingen doorgevoerd voor het GC-subcomponent in JDK 27. Dit aantal komt overeen met de vorige release; ook toen was het aantal wellicht enigszins opgeblazen door een ongebruikelijk grote hoeveelheid refactorings en opschoning van de GC-code.
Een specifiek onderdeel hiervan was het vervangen van de methode om variabelen die gedeeld worden tussen threads aan te geven. In plaats van volatile en de bijbehorende AtomicAccess-methoden, wordt nu gebruikgemaakt van Atomic<T> (geïntroduceerd in JDK-8367013) om het gebruik explicieter te maken. Andere opvallende refactorings zijn de opschoning van de G1 state machine en diverse overige codeverbeteringen. Deze vormen samen ongeveer de helft van alle wijzigingen.
Ongeveer 35% van de wijzigingen heeft betrekking op bugfixes en algemene verbeteringen aan de correctheid en robuustheid. De rest bestaat uit nieuw of materieel herzien gedrag en functies voor de STW-collectors van HotSpot. Daarnaast is er infrastructuurwerk verricht voor JEP 401: Value Objects (Preview), zoals het aanpassen van object-iterators en eager reclaim voor value objects.
G1 GC
De meest impactvolle wijziging in deze release is waarschijnlijk JEP 523: Make G1 the Default Garbage Collector in All Environments. G1 is nu de standaard collector wanneer er geen specifiek garbage collection-algoritme via de opdrachtregel wordt opgegeven. Er zijn geen uitzonderingen meer; Serial GC is in geen enkel geval meer de standaard (tenzij G1 niet is opgenomen in jouw distributie van OpenJDK). De keuze voor G1 als standaardcollector is gemaakt omdat het goed aansluit bij de behoeften van een default collector, terwijl het selecteren van een alternatief op basis van obscure omgevingsfactoren eerder een last dan een voordeel was. Doorstroom (throughput), voetafdruk en latentieprofiel zijn in de afgelopen releases stap voor stap dichter bij die van Serial GC gekomen, waardoor het moment daar is gekomen om over te stappen.
Men kan nog steeds Serial GC selecteren met de optie -XX:+UseSerialGC indien er merkbare verschillen worden geconstateerd of wanneer men pure, ongewijzigde Serial GC wenst. Er zijn immers gevallen waarin Serial GC voor een specifiek applicatieprofiel de betere keuze is; G1 is niet overal de beste, maar is wel het beste startpunt.
Andere wijzigingen aan G1:
- Heap-dimensionering: G1 past de heap na een Full GC niet langer aan op basis van de (arbitraire) percentages van
-XX:MinHeapFreeRatio en -XX:MaxHeapFreeRatio. De standaardwaarden hiervan zijn gewijzigd naar respectievelijk 0% en 100% (voorheen 40% en 70%), zodat ze geen invloed hebben op reguliere heuristieken voor dimensionering (zoals het CPU-gebruik van de garbage collector ten opzichte van dat van de applicatie).
- De oorspronkelijke standaardwaarden zorgden ervoor dat deze heuristiek botste met andere methoden, wat leidde tot onnodige wijzigingen in de Java heap-grootte. Dit veroorzaakte prestatieproblemen wanneer een volledige heap garbage collection de heap verkleinde, waarna andere heuristieken deze wijzigingen vrijwel direct ongedaan maakten.
- De functionaliteit van deze vlaggen blijft overigens behouden.
- Concurrent marking: De adaptieve start van concurrent marking is verbeterd om beter bestand te zijn tegen ongunstige omstandigheden die voorheen leidden tot onnodig continu concurrent werk (JDK-8379846, JDK-8381006).
- Humongous objects: Voorheen werden humongous objects die eigenlijk konden worden teruggewonnen, onbedoeld in leven gehouden door weak references (bijv.
java.lang.ref.Reference-instanties). Dit is opgelost met JDK-8378331 en JDK-8378336.
- Kleine observeerbare wijzigingen:
- G1 Cleanup-pauzes updaten
MemoryPoolMXBean.getCollectionUsage() niet meer, aangezien ze de Java heap niet wijzigen (JDK-8386332).
- In JDK-8373894 worden garbage collections die onvoldoende ruimte vonden om naar te kopiëren nu wel meegeteld in het CPU-gebruik van de garbage collector. Dit verbetert de besluitvorming over de heap-dimensionering.
Parallel GC
Voor Parallel GC zijn er enkele belangrijke bugfixes doorgevoerd die de prestaties kunnen verbeteren:
- Adaptive tenuring threshold: Deze drempel bepaalt hoeveel young collections een object mag blijven in de young generation. Voorheen neigde deze drempel ernaar om alleen te stijgen en zelden te dalen. Een aanhoudend hoge drempel kon ertoe leiden dat oudere objecten een groot deel van de survivor space in beslag namen, waardoor jongere objecten voortijdig werden gepromoveerd wanneer deze ruimte vol was. Met JDK-8380590 kan de drempel nu ook dalen, waardoor langer levende objecten eerder worden gepromoveerd en jongere objecten die waarschijnlijk snel zullen sterven in de young generation behouden blijven.
- Heap-uitbreiding: Parallel GC breidt de Java heap nu correct uit wanneer herhaalde grote allocaties duizenden Full GC's veroorzaken terwijl de Java heap niet groeide, zelfs als er voldoende ruimte beschikbaar was (JDK-8377561).
Serial GC
Er zijn geen noemenswaardige specifieke wijzigingen voor Serial GC, afgezien van het feit dat het in sommige gevallen niet langer de standaardcollector is.
Alle Collectors
Voor alle collectors zijn de volgende interessante wijzigingen doorgevoerd:
- TLAB-dimensionering: De dimensionering van TLAB's (Thread Local Allocation Buffers) gaat nu beter om met applicaties die veel kortstondige threads aanmaken met lichte allocaties. Dit voorkomt te grote TLAB's die leiden tot veel verspilling, wat het aantal collecties verlaagt (JDK-8381834).
- String deduplicatie: JDK-8372348 verbetert de logging en JFR-events voor string deduplication. De logging voegt nu "new unknown" strings toe en geeft betekenisvollere geaggregeerde byte-aantallen.
Wat volgt er nu?
De focus blijft liggen op Automatic Heap Sizing en het verbeteren van de controle over verschillende aspecten van het geheugenbeheer, zodat G1 intelligenter kan reageren op externe omstandigheden en de intenties van de gebruiker.
JDK 27 wijzigingen in G1/Parallel/Serial GC
In totaal zijn er ongeveer 350 wijzigingen doorgevoerd voor het GC-subcomponent in JDK 27. Dit aantal komt overeen met de vorige release; ook toen was het aantal wellicht enigszins opgeblazen door een ongebruikelijk grote hoeveelheid refactorings en opschoning van de GC-code.
Een specifiek onderdeel hiervan was het vervangen van de methode om variabelen die gedeeld worden tussen threads aan te geven. In plaats van volatile en de bijbehorende AtomicAccess-methoden, wordt nu gebruikgemaakt van Atomic<T> (geïntroduceerd in JDK-8367013) om het gebruik explicieter te maken. Andere opvallende refactorings zijn de opschoning van de G1 state machine en diverse overige codeverbeteringen. Deze vormen samen ongeveer de helft van alle wijzigingen.
Ongeveer 35% van de wijzigingen heeft betrekking op bugfixes en algemene verbeteringen aan de correctheid en robuustheid. De rest bestaat uit nieuw of materieel herzien gedrag en functies voor de STW-collectors van HotSpot. Daarnaast is er infrastructuurwerk verricht voor JEP 401: Value Objects (Preview), zoals het aanpassen van object-iterators en eager reclaim voor value objects.
G1 GC
De meest impactvolle wijziging in deze release is waarschijnlijk JEP 523: Make G1 the Default Garbage Collector in All Environments. G1 is nu de standaard collector wanneer er geen specifiek garbage collection-algoritme via de opdrachtregel wordt opgegeven. Er zijn geen uitzonderingen meer; Serial GC is in geen enkel geval meer de standaard (tenzij G1 niet is opgenomen in jouw distributie van OpenJDK). De keuze voor G1 als standaardcollector is gemaakt omdat het goed aansluit bij de behoeften van een default collector, terwijl het selecteren van een alternatief op basis van obscure omgevingsfactoren eerder een last dan een voordeel was. Doorstroom (throughput), voetafdruk en latentieprofiel zijn in de afgelopen releases stap voor stap dichter bij die van Serial GC gekomen, waardoor het moment daar is gekomen om over te stappen.
Men kan nog steeds Serial GC selecteren met de optie -XX:+UseSerialGC indien er merkbare verschillen worden geconstateerd of wanneer men pure, ongewijzigde Serial GC wenst. Er zijn immers gevallen waarin Serial GC voor een specifiek applicatieprofiel de betere keuze is; G1 is niet overal de beste, maar is wel het beste startpunt.
Andere wijzigingen aan G1:
- Heap-dimensionering: G1 past de heap na een Full GC niet langer aan op basis van de (arbitraire) percentages van
-XX:MinHeapFreeRatio en -XX:MaxHeapFreeRatio. De standaardwaarden hiervan zijn gewijzigd naar respectievelijk 0% en 100% (voorheen 40% en 70%), zodat ze geen invloed hebben op reguliere heuristieken voor dimensionering (zoals het CPU-gebruik van de garbage collector ten opzichte van dat van de applicatie).
- De oorspronkelijke standaardwaarden zorgden ervoor dat deze heuristiek botste met andere methoden, wat leidde tot onnodige wijzigingen in de Java heap-grootte. Dit veroorzaakte prestatieproblemen wanneer een volledige heap garbage collection de heap verkleinde, waarna andere heuristieken deze wijzigingen vrijwel direct ongedaan maakten.
- De functionaliteit van deze vlaggen blijft overigens behouden.
- Concurrent marking: De adaptieve start van concurrent marking is verbeterd om beter bestand te zijn tegen ongunstige omstandigheden die voorheen leidden tot onnodig continu concurrent werk (JDK-8379846, JDK-8381006).
- Humongous objects: Voorheen werden humongous objects die eigenlijk konden worden teruggewonnen, onbedoeld in leven gehouden door weak references (bijv.
java.lang.ref.Reference-instanties). Dit is opgelost met JDK-8378331 en JDK-8378336.
- Kleine observeerbare wijzigingen:
- G1 Cleanup-pauzes updaten
MemoryPoolMXBean.getCollectionUsage() niet meer, aangezien ze de Java heap niet wijzigen (JDK-8386332).
- In JDK-8373894 worden garbage collections die onvoldoende ruimte vonden om naar te kopiëren nu wel meegeteld in het CPU-gebruik van de garbage collector. Dit verbetert de besluitvorming over de heap-dimensionering.
Parallel GC
Voor Parallel GC zijn er enkele belangrijke bugfixes doorgevoerd die de prestaties kunnen verbeteren:
- Adaptive tenuring threshold: Deze drempel bepaalt hoeveel young collections een object mag blijven in de young generation. Voorheen neigde deze drempel ernaar om alleen te stijgen en zelden te dalen. Een aanhoudend hoge drempel kon ertoe leiden dat oudere objecten een groot deel van de survivor space in beslag namen, waardoor jongere objecten voortijdig werden gepromoveerd wanneer deze ruimte vol was. Met JDK-8380590 kan de drempel nu ook dalen, waardoor langer levende objecten eerder worden gepromoveerd en jongere objecten die waarschijnlijk snel zullen sterven in de young generation behouden blijven.
- Heap-uitbreiding: Parallel GC breidt de Java heap nu correct uit wanneer herhaalde grote allocaties duizenden Full GC's veroorzaken terwijl de Java heap niet groeide, zelfs als er voldoende ruimte beschikbaar was (JDK-8377561).
Serial GC
Er zijn geen noemenswaardige specifieke wijzigingen voor Serial GC, afgezien van het feit dat het in sommige gevallen niet langer de standaardcollector is.
Alle Collectors
Voor alle collectors zijn de volgende interessante wijzigingen doorgevoerd:
- TLAB-dimensionering: De dimensionering van TLAB's (Thread Local Allocation Buffers) gaat nu beter om met applicaties die veel kortstondige threads aanmaken met lichte allocaties. Dit voorkomt te grote TLAB's die leiden tot veel verspilling, wat het aantal collecties verlaagt (JDK-8381834).
- String deduplicatie: JDK-8372348 verbetert de logging en JFR-events voor string deduplication. De logging voegt nu "new unknown" strings toe en geeft betekenisvollere geaggregeerde byte-aantallen.
Wat volgt er nu?
De focus blijft liggen op Automatic Heap Sizing en het verbeteren van de controle over verschillende aspecten van het geheugenbeheer, zodat G1 intelligenter kan reageren op externe omstandigheden en de intenties van de gebruiker.