PerfectScalePerfectScale

PerfectScale

Ihr JVM-Service braucht 2 CPUs für 30 Sekunden – und 0,3 CPUs für immer

Eine JVM braucht in den ersten Sekunden die meiste CPU. Erfahren Sie, wie Kubernetes-CPU-Limits den JVM-Start drosseln, wie Sie das messen und Ihre Pods dimensionieren.

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

Tania Duggal
By Tania Duggal
Oct 14, 202618 min read

Ein Java-Service braucht die meiste CPU in seinen ersten Sekunden. Die JVM lädt Klassen, kompiliert Code und baut die Anwendung auf. Danach braucht der Service deutlich weniger CPU. In den meisten Clustern behält ein Pod denselben CPU-Request und dasselbe CPU-Limit für seine gesamte Lebensdauer. Der Pod kann diesem Muster daher nicht folgen.

Dieses Missverhältnis führt zu Kubernetes-CPU-Throttling während des JVM-Starts. Ist das Limit niedrig, kann die JVM ihre Startarbeit nicht schnell abschließen, und der Pod wird erst spät Ready. Ist der Request hoch, reserviert der Node CPU, die der Service nach dem Start nicht nutzt. Keine der beiden Einstellungen passt zu beiden Phasen.

Die Zahlen im Titel sind ein Beispiel. Ihr eigener Service hat seine eigenen Zahlen, und dieser Artikel zeigt Ihnen, wie Sie prüfen, was Ihr Service tatsächlich tut. Der Artikel erklärt, warum der Start mehr CPU braucht, wie Kubernetes CPU-Einstellungen durchsetzt und was die JVM aus diesen Einstellungen ableitet. Am Ende finden Sie die Optionen, die Ihnen heute zur Verfügung stehen.

Warum braucht der Java-Start mehr CPU als der Steady State?

Bevor eine JVM ihre erste Anfrage bedient, muss sie eine große Menge Arbeit erledigen. Diese Arbeit besteht aus vier Hauptteilen:

Laden und Linken von Klassen

Die JVM lädt eine Klasse, wenn der Code sie zum ersten Mal benötigt. Sie liest die Klassendatei, prüft sie und linkt sie. Eine Spring-Anwendung benötigt in den ersten Sekunden eine große Zahl von Klassen, weil das Framework die Anwendung scannt und seine Beans erzeugt.

JIT-Kompilierung mit C1 und C2

Die JVM beginnt damit, Bytecode zu interpretieren, was langsam ist. Sie zählt, wie oft jede Methode ausgeführt wird. HotSpot kompiliert die häufig genutzten Methoden anschließend mit zwei Compilern zu Maschinencode. C1 kompiliert schnell und erzeugt einfachen Code. C2 kompiliert langsamer und erzeugt schnelleren Code für die am stärksten genutzten Methoden. Dieses Design heißt Tiered Compilation. Die Compiler laufen in eigenen Threads, und die JVM wählt die Standardanzahl dieser Threads anhand der CPU-Anzahl.

Garbage-Collection-Threads und Heap-Wachstum

Der Start alloziert viele Objekte. Der Heap wächst, und der Collector läuft, um Speicher freizugeben. Die JVM wählt den Collector und dessen Thread-Anzahl anhand der CPU-Anzahl und der Speichergröße.

Framework-Initialisierung

Das Framework baut Beans auf, erzeugt Proxies, liest die Konfiguration und öffnet Verbindungen. Spring Boot Actuator kann jeden dieser Schritte in seinem Startup-Endpoint auflisten, wenn Sie BufferingApplicationStartup verwenden – so sehen Sie, wo die Zeit bleibt.

Jeder Teil hat ein Ende. Sobald die Klassen geladen, die häufig genutzten Methoden kompiliert und die Beans erzeugt sind, sinkt der CPU-Bedarf. Der Rückgang erfolgt nicht schlagartig, weil die JIT-Compiler weiter Methoden kompilieren, wenn echter Traffic eintrifft. Dennoch braucht der Steady State deutlich weniger CPU als der Start. Diese Lücke zwischen den beiden Phasen ist der Grund, warum eine einzige CPU-Einstellung nicht zu beiden passen kann.

Wie setzt Kubernetes CPU-Requests und -Limits durch?

Kubernetes steuert die CPU eines Containers über zwei Einstellungen:

CPU-Requests und Scheduling

Der CPU-Request ist die CPU-Menge, die Kubernetes für einen Container reserviert. Der Scheduler summiert die Requests der Pods auf jedem Node und platziert einen neuen Pod nur auf einem Node mit genügend nicht reservierter CPU. Der Request bestimmt außerdem den CPU-Anteil auf einem ausgelasteten Node. Wenn Container um CPU konkurrieren, gibt der Kernel einem Container mit höherem Request mehr CPU-Zeit. Unter cgroup v2 schreibt die Container-Runtime den Request als cpu.weight. Unter cgroup v1 verwendet sie cpu.shares.

Setzen Sie ein Limit, aber keinen Request, verwendet Kubernetes das Limit als Request. Request und Limit entscheiden auch über die QoS-Klasse des Pods. Ein Pod ist Guaranteed, wenn jeder Container denselben Wert für CPU-Request und CPU-Limit sowie denselben Wert für Memory-Request und Memory-Limit hat. Er ist Burstable, wenn er einige Requests oder Limits hat, aber die Guaranteed-Regel nicht erfüllt. Er ist BestEffort, wenn er keine hat.

CPU-Limits und Throttling

Das CPU-Limit ist eine harte Obergrenze, und der Kernel setzt sie per Throttling durch. Der Kernel teilt die Zeit in Perioden ein, und das kubelet verwendet standardmäßig eine Periode von 100 ms. In jeder Periode darf der Container eine Quota an CPU-Zeit nutzen. Das Limit bestimmt die Größe der Quota. Die Runtime schreibt das Limit unter cgroup v2 als cpu.max. Ein Limit von 500m wird zu 50000 100000 – eine Quota von 50.000 Mikrosekunden in jeder Periode von 100.000 Mikrosekunden.

resources:
requests:
cpu: 300m # reserviert CPU und bestimmt den CPU-Anteil auf einem ausgelasteten Node
memory: 2Gi
limits:
cpu: "1" # harte Obergrenze: wird als 100000 100000 in cpu.max geschrieben
memory: 2Gi

Was ist CPU-Throttling in Kubernetes?

Throttling ist die Art, wie der Kernel ein CPU-Limit durchsetzt. Wenn die Threads eines Containers die gesamte Quota einer Periode aufbrauchen, pausiert der Kernel alle Threads, bis die nächste Periode beginnt.

Die Threads teilen sich eine Quota, und dieses Detail ist für eine JVM entscheidend. Nehmen Sie ein Limit von 500m, das 50 ms CPU-Zeit pro Periode gibt. Ein ausgelasteter Thread braucht 50 ms, um die Quota zu verbrauchen, läuft also die halbe Periode. Acht ausgelastete Threads verbrauchen dieselbe Quota in etwa 6 ms, und dann pausiert der Kernel alle acht für den Rest der Periode. Eine JVM führt beim Start viele ausgelastete Threads gleichzeitig aus und erreicht ihre Quota daher früh in jeder Periode.

Kubernetes stoppt einen Container nicht, weil er zu viel CPU nutzt. Es bremst ihn aus. Kubernetes meldet Throttling nicht als Event, und die Container-Logs zeigen keinen Fehler. Der Schaden zeigt sich als Verzögerung, und Sie finden ihn nur, wenn Sie ihn messen.

CPU-Throttling während des JVM-Starts

Ein großer Teil des JVM-Starts ist CPU-gebundene Arbeit. Die JVM muss eine feste Menge Arbeit erledigen, bevor die Anwendung Traffic bedienen kann, und das Limit bestimmt, wie schnell der Container diese Arbeit erledigen kann. Ein höheres Limit beendet die Arbeit früher. Ein niedrigeres Limit streckt dieselbe Arbeit über mehr Perioden, und der Container wartet in jeder Periode, sobald er seine Quota verbraucht hat. Throttling während des Starts verursacht zwei Probleme:

Langsamer Start und späte Readiness

Eine gedrosselte JVM braucht länger zum Starten, der Pod wird also später Ready. Diese Verzögerung verursacht Kosten außerhalb des Pods. Ein Rolling Update wartet, bis jeder neue Pod Ready ist, sodass jedes Rollout länger dauert. Neue Replicas helfen erst, wenn sie Ready sind, sodass das Scale-out bei wachsendem Traffic langsamer ist. Bei einem Node-Ausfall benötigen die Ersatz-Pods ebenfalls dieselbe zusätzliche Zeit.

Fehlgeschlagene Probes und Neustarts

Eine Startup-Probe teilt dem kubelet mit, wann die Anwendung gestartet ist. Während die Startup-Probe läuft, führt Kubernetes die Liveness- und Readiness-Probes nicht aus. Wenn die Startup-Probe nicht innerhalb von failureThreshold × periodSeconds erfolgreich ist, startet das kubelet den Container neu. Verwenden Sie eine Startup-Probe für einen Container, der lange zum Starten braucht, und richten Sie sie auf denselben Endpoint wie die Liveness-Probe.

Ohne Startup-Probe kann ein langsamer Start eine Neustart-Schleife verursachen. Die Liveness-Probe schlägt fehl, während die JVM noch startet, das kubelet startet den Container neu, und der neue Container wiederholt dieselbe CPU-Arbeit. Eine gedrosselte JVM macht diese Schleife wahrscheinlicher, weil ihr Start länger dauert.

Eine Startup-Probe mit großzügigem Zeitfenster verhindert die Schleife:

startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 5
failureThreshold: 24 # wartet bis zu 24 x 5 = 120 Sekunden

Ein großzügiges Zeitfenster stoppt die Neustarts, macht den Start aber nicht schneller. Der Pod wird weiterhin spät Ready.

Wie misst man Throttling?

Zwei cAdvisor-Counter zeigen Throttling. Der Counter container_cpu_cfs_periods_total zählt alle Perioden. Der Counter container_cpu_cfs_throttled_periods_total zählt die Perioden, in denen der Container seine gesamte Quota verbraucht hat. Teilen Sie den zweiten Counter durch den ersten, um den Anteil der gedrosselten Perioden zu erhalten.

sum(rate(container_cpu_cfs_throttled_periods_total{container="app"}[5m]))
/
sum(rate(container_cpu_cfs_periods_total{container="app"}[5m]))

Eine Fünf-Minuten-Rate verbirgt einen Start, der nur wenige Sekunden dauert. Lesen Sie für den Start die rohen Counter, sobald der Pod Ready wird, und teilen Sie sie selbst. Unter cgroup v2 stellt der Container dieselben Daten außerdem in cpu.stat bereit. Die Felder nr_periods, nr_throttled und throttled_usec zeigen die Anzahl der Perioden, die Anzahl der gedrosselten Perioden und die gesamte gedrosselte Zeit.

Terminal window
kubectl exec <pod-name> -- cat /sys/fs/cgroup/cpu.stat

Sie können die cAdvisor-Werte auch vom kubelet abrufen:

Terminal window
kubectl get --raw /api/v1/nodes/<node-name>/proxy/metrics/cadvisor | grep container_cpu_cfs

Denken Sie an das Sampling-Intervall. Das Metriksystem aktualisiert seine Werte nur in einem festen Intervall. Ein Startpeak, der kürzer als dieses Intervall ist, wirkt kleiner, als er ist, oder erscheint gar nicht. Samplen Sie bei kurzen Peaks die rohen Counter häufiger.

Wie das CPU-Limit das Verhalten der JVM verändert

Das CPU-Limit bremst die JVM nicht nur aus. Unter Linux unterstützt die JVM Container standardmäßig (-XX:+UseContainerSupport). Sie liest das Limit beim Start. Diese drei Punkte zeigen, wie dieser Wert das Verhalten der JVM verändert:

Aktive Prozessoranzahl aus CPU-Limits

Die JVM teilt die CPU-Quota durch die Periode und rundet das Ergebnis auf die nächste ganze Zahl auf. Ein Limit von 1001m ergibt daher 2 CPUs. JDK 19 und neuer sowie JDK 17.0.5 und neuer verwenden den CPU-Request (cpu.shares) nicht, um CPUs zu zählen. Viele JVM-Einstellungen hängen von dieser Anzahl ab, darunter die Anzahl der JIT-Compiler-Threads und die Anzahl der GC-Threads.

Auswahl des Garbage Collectors (Serial vs. G1)

Die JVM behandelt eine Maschine als server-class, wenn sie mindestens 2 Prozessoren und mindestens 1792 MB Speicher hat. Auf einer server-class-Maschine wählt die JVM G1. Andernfalls wählt sie den Serial Collector. Eine kleine Änderung am Limit kann daher den Collector ändern. Ein Limit von 1 CPU ergibt einen Prozessor, also wählt die JVM Serial. Ein Limit von 1100m ergibt zwei Prozessoren, also wählt die JVM G1, wenn der Speicher mindestens 1792 MB beträgt. Die beiden Collectors verhalten sich beim Start unterschiedlich. G1 startet mehrere Worker-Threads, die parallel laufen, und kann daher mehr CPU gleichzeitig nutzen. Serial sammelt mit einem einzigen Thread.

Pods ohne CPU-Limits

Ohne Limit zählt die JVM die CPUs des Nodes. Auf einem großen Node sieht sie viele CPUs und startet viele Compiler- und GC-Threads. Der Pod wird dann nicht gedrosselt, kann aber insgesamt viel CPU nutzen und konkurriert mit seinen Nachbarn proportional zu seinem Request. Ein kleiner Request gibt diesem Pod einen kleinen Anteil, wenn der Node ausgelastet ist.

Sie können die Entscheidungen Ihrer eigenen JVM prüfen. Aktivieren Sie das Container-Log und lesen Sie die Flags der laufenden JVM:

env:
- name: JAVA_TOOL_OPTIONS
value: "-Xlog:os+container=trace -Xlog:gc*"
Terminal window
kubectl exec <pod-name> -- jcmd 1 VM.flags -all \
| grep -E 'UseSerialGC|UseG1GC|ParallelGCThreads|CICompilerCount'

Hinweis: Der Befehl benötigt jcmd im Image und erwartet, dass die JVM als Prozess 1 läuft. Wenn eine Shell die JVM startet, verwenden Sie die tatsächliche Prozess-ID der JVM. Ein reines JRE-Image enthält jcmd nicht.

Das Flag -XX:ActiveProcessorCount=<n> überschreibt die erkannte CPU-Anzahl. Verwenden Sie es, wenn Sie in jeder Umgebung dasselbe JVM-Verhalten wollen.

Warum kein einzelner CPU-Request sicher ist

Jede Art, den Request zu setzen, hat ihren Preis. Das sind die vier Möglichkeiten:

Dimensionierung für den Start

Ein hoher Request mit hohem Limit gibt der JVM genug CPU für einen schnellen Start. Aber der Node reserviert den hohen Request für die gesamte Lebensdauer des Pods, und andere Pods können diese CPU nicht nutzen. Nach dem Start nutzt der Service nur einen kleinen Teil der Reservierung. Über viele Replicas und viele Nodes hinweg wird diese ungenutzte CPU zu einem erheblichen Kostenfaktor.

Dimensionierung für den Steady State

Ein kleiner Request mit kleinem Limit passt zum Service nach dem Start. Aber die JVM hat jetzt zu wenig CPU für ihre Startarbeit. Der Kernel drosselt sie, der Pod wird spät Ready, Probes können fehlschlagen, und Rollouts dauern länger. Der Service ist günstig, aber fragil.

CPU-Limit entfernen

Ohne Limit drosselt der Kernel den Container nicht, und der Start kann jede freie CPU auf dem Node nutzen. Diese Option bringt Kompromisse mit sich. Die JVM dimensioniert ihre Threads für alle CPUs des Nodes. Auf einem ausgelasteten Node bekommt der Container CPU-Zeit nur proportional zu seinem Request, ein kleiner Request kann den Start also weiterhin verlangsamen. Außerdem kann ein Pod ohne Limit CPU beanspruchen, die seine Nachbarn brauchen.

HPA und die CPU-Initialisierungsperiode

Der Horizontal Pod Autoscaler (HPA) misst die CPU-Nutzung als Prozentsatz des Requests. Ein kleiner Request lässt den Startpeak wie sehr hohe Last aussehen, und der HPA kann Replicas hinzufügen, die der Service nicht braucht. Kubernetes verringert dieses Risiko, weil der HPA CPU-Samples für einen neuen Pod überspringen kann. Standardmäßig beträgt die Readiness-Verzögerung 30 Sekunden (--horizontal-pod-autoscaler-initial-readiness-delay) und die CPU-Initialisierungsperiode 5 Minuten (--horizontal-pod-autoscaler-cpu-initialization-period). Beide Flags gehören zum kube-controller-manager, in einem Managed Cluster können Sie sie daher möglicherweise nicht ändern.

So gehen Sie heute mit der Startup-CPU um

Diese Möglichkeiten haben Sie, um die Startup-CPU in den Griff zu bekommen:

Startarbeit reduzieren

Weniger Arbeit bedeutet einen kürzeren Start bei jedem Limit. Spring Boot kann Beans mit spring.main.lazy-initialization=true erst bei der ersten Nutzung erzeugen. Class Data Sharing und der AOT-Cache können einen Teil der Klassenladearbeit entfernen. Der AOT-Cache kam mit JDK 24 (JEP 483). Das Spring Framework unterstützt sowohl CDS als auch den AOT-Cache. Profiling zeigt, wo Ihre eigene Startzeit bleibt.

CPU-Limit über dem Startbedarf setzen

Halten Sie den Request nahe am Steady-State-Niveau und setzen Sie das Limit hoch genug für den Start. Der Pod hat dann die QoS-Klasse Burstable. Der Scheduler reserviert nur den Request, und der Pod kann CPU bis zum Limit nutzen, wenn der Node freie CPU hat. Beim Start hat der Node möglicherweise keine freie CPU, diese Option hilft also am meisten auf Nodes mit Reservekapazität. Ein höheres Limit ändert außerdem die CPU-Anzahl, die die JVM sieht.

In-Place Pod Resize

In-Place Pod Resize ändert CPU und Speicher eines laufenden Pods ohne Neustart. Das Feature erreichte die Beta in Kubernetes 1.33 und wurde in Kubernetes 1.35 stabil. Mit diesem Feature kann ein Pod mit einem hohen CPU-Limit starten und nach dem Start auf ein niedrigeres wechseln. Ein Container steuert das Verhalten über eine Resize Policy. Die Standard-Policy für CPU ist NotRequired, und Sie können sie im Manifest angeben:

containers:
- name: app
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired

Sie ändern die Ressourcen über die Subresource resize. Dieser Befehl benötigt kubectl 1.32 oder neuer:

Terminal window
kubectl patch pod <pod-name> --subresource resize --patch \
'{"spec":{"containers":[{"name":"app","resources":{"requests":{"cpu":"300m"},"limits":{"cpu":"500m"}}}]}}'

Das Feld status.containerStatuses[].resources zeigt die Ressourcen, die der Container aktuell nutzt. Einige Pods können In-Place Resize nicht nutzen. Windows-Pods und Pods unter der statischen CPU- oder Memory-Manager-Policy sind Beispiele. Ein Resize kann außerdem die QoS-Klasse des Pods nicht ändern. Und jemand muss das Resize auslösen. Eine Person kann den Patch manuell ausführen, aber für viele Pods muss ein Controller das übernehmen.

Ein Resize ändert das cgroup-Limit, aber die JVM trifft manche Entscheidungen nur einmal. Sie wählt den Collector beim Start und behält ihn. Einstellungen, die von der CPU-Anzahl abhängen, etwa die maximale Anzahl der GC-Threads, werden ebenfalls beim Start festgelegt. Runtime.availableProcessors() kann sich zur Laufzeit ändern, Code, der die CPU-Anzahl zur Laufzeit liest, kann den neuen Wert also sehen. Testen Sie ein Resize mit Ihrem eigenen JDK, bevor Sie sich darauf verlassen.

Vorsicht beim JVM-Speicher

Senken Sie das Memory-Limit eines JVM-Containers nicht. Die JVM legt ihren maximalen Heap beim Start fest. Standardmäßig beträgt der maximale Heap 25 % des Container-Speichers (-XX:MaxRAMPercentage). Die JVM nutzt auch Speicher außerhalb des Heaps, etwa Metaspace, Thread-Stacks und den Code-Cache. Ein Memory-Limit unter dem tatsächlichen Verbrauch des Prozesses verursacht einen OOM-Kill. Passen Sie deshalb nach dem Start nur die CPU an, und erhöhen Sie das Memory-Limit nur bei Bedarf.

VPA und Managed-Startup-Boost-Optionen

Der Vertical Pod Autoscaler (VPA) hat den Update-Modus InPlaceOrRecreate. Dieser Modus versucht zuerst ein In-Place Resize und erstellt den Pod neu, wenn ein In-Place Resize nicht möglich ist. Auf GKE funktioniert der CPU-Startup-Boost mit dem VPA. Er erhöht den CPU-Request beim Erstellen des Pods und behält den höheren Request für durationSeconds, nachdem der Pod Ready wurde. Danach setzt GKE den Request in-place auf seinen Basiswert zurück. Der Boost gilt nur beim Erstellen des Pods, ein Container-Neustart innerhalb desselben Pods bekommt also keinen Boost.

Das Feature ist zum Zeitpunkt der Erstellung dieses Artikels in der Preview und benötigt GKE 1.36.0-gke.4447000 oder neuer.

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: example-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: example
updatePolicy:
updateMode: "Off"
startupBoost:
cpu:
type: "Factor"
factor: 2
durationSeconds: 10

In diesem Beispiel verdoppelt der Boost den CPU-Request (factor: 2) und behält ihn 10 Sekunden lang, nachdem der Pod Ready wurde. Mit updateMode auf Off wendet der VPA nur den Boost an und nimmt keine weiteren Änderungen vor. Wenn Sie einen HPA zusammen mit dem Boost verwenden, definieren Sie eine Readiness-Probe und setzen Sie durationSeconds auf 0. Das verhindert, dass der HPA wegen des CPU-Peaks beim Start Replicas hinzufügt.

Zweiphasige Automatisierung mit PerfectScale

Ein JVM-Service hat zweierlei Ressourcenbedarf. Beim Start lädt die JVM Klassen, kompiliert Code und baut die Anwendung auf, der Service braucht also viel CPU. Danach braucht er deutlich weniger. Eine einzige Durchschnittsempfehlung funktioniert nicht für beide Phasen. PerfectScale liefert zwei Empfehlungen pro Workload: eine für den Start und eine für den Steady-State-Betrieb. PerfectScale dimensioniert die Startempfehlung nach dem höheren Bedarf der beiden Phasen, damit der Node während des Starts genügend Kapazität hat.

PerfectScale wendet die Startempfehlung beim Erstellen des Pods an. Sobald der Pod stabil läuft, wendet PerfectScale die Laufzeitempfehlung in-place an, ohne Neustart. PerfectScale verfolgt die Phase jedes Pods und fügt Sicherheitsprüfungen hinzu, wenn ein Workload nicht sauber in die Laufzeitphase wechseln kann.

In-Place Resize benötigt Kubernetes 1.33 oder neuer und die Container-Resize-Policy restartPolicy: NotRequired. Es unterstützt Deployments, StatefulSets, DaemonSets und Argo Rollouts; Windows-Pods werden nicht unterstützt. Wenn das Cluster In-Place Resize nicht unterstützt, greift PerfectScale auf seinen standardmäßigen, sicheren Ansatz zurück.

PerfectScale schützt den JVM-Heap. Wenn Sie eine maximale oder minimale Heap-Größe festlegen, wendet PerfectScale keine Empfehlung an, die außerhalb davon liegt. Bei In-Place Resize kann das Memory-Limit nur steigen. Für Java-Container sammelt PerfectScale außerdem Heap-, Non-Heap- und Garbage-Collection-Daten. Es zeigt die GC-Zeit, die aktive Prozessoranzahl und den GC-Typ. Sie können die Erfassung von JVM-Metriken bei der Installation des PerfectScale-Agents aktivieren; sie unterstützt nur Linux-Nodes. PerfectScale zeigt Start- und Steady-State-Ressourcen getrennt an, sodass die Kosten- und Waste-Zahlen widerspiegeln, was der Workload in jeder Phase tatsächlich benötigt hat.

Nehmen Sie am Workshop teil. Vikram Seshadri von DoiT zeigt im Hands-on-Workshop "Java on K8s: The Startup vs. Steady-State Sizing Dilemma" am 28. Oktober 2026 um 11 Uhr ET, wie Sie einen JVM-Pod für seinen Start dimensionieren und ihn ohne Neustart auf den Steady State anpassen. Sichern Sie sich Ihren Platz.

Möchten Sie sehen, wie PerfectScale Ihre Java-Workloads per Right-Sizing optimiert? Starten Sie kostenlos oder buchen Sie eine Demo.