Skip to main content

Dimensionierung und JVM-Konfiguration

Die meisten Einstellungen des jadice web toolkit sind sinnvoll voreingestellt. Mindestens die Dimensionierung der Server und die Konfiguration der Caches müssen jedoch zur erwarteten Last passen. Die folgenden Werte sind Erfahrungswerte und dienen als Startpunkt. Verbindlich werden sie erst durch einen Performance-Test mit dem eigenen Dokumentenbestand. Gerne unterstützt Sie dabei der jadice support.

Hintergründe zum Ressourcenverbrauch beschreibt das Kapitel Dimensionierung der Hardware und Caching.

Server-Instanzen​

Erfahrungswerte für 500–1000 gleichzeitige Benutzer:

RessourceRichtwert
Instanzen (Pods/VM)2–4; mit 2 Instanzen starten und bei Bedarf hochskalieren
CPU4 logische Kerne pro Instanz, im Container als CPU-Limit gesetzt
RAM(6–)8 GB pro Instanz
Heap(4–)6 GB, z. B. über -XX:MaxRAMPercentage=75.0 (siehe JVM-Parameter)

Ab zwei Instanzen ist ein Load-Balancer mit Session Affinity erforderlich.

Ohne CPU-Limit ermittelt die JVM im Container unter Umständen nur einen Prozessor, und die Thread-Pools starten entsprechend klein (siehe Betrieb in einer Container-Umgebung).

Die passende Dimensionierung hängt stark vom Dokumentenbestand ab:

  • PDF kostet gegenüber anderen Formaten mehr Rechenzeit. Planen Sie weniger Benutzer bzw. mehr Prozessoren pro Instanz ein.
  • Große Dokumente (viele Seiten, große Formate wie DIN A0, hohe Auflösung) benötigen mehr Heap. Die Rasterung erfolgt in Java; eine A4-Seite bei 300 dpi in ARGB belegt rund 35 MB Heap, bei 600 dpi rund 139 MB – pro gleichzeitig gehaltener Seite.

Tile-Format im Container​

Verwenden Sie im Container-Betrieb ein PNG-basiertes Tile-Format (Default PNGJ_BEST_SPEED oder IMAGEIO) und kein WebP. Die Erzeugung von WebP-Bildern benötigt Off-Heap-Speicher, der im JVM-Monitoring kaum sichtbar ist und sich schlecht begrenzen lässt. Mit PNG kann die Lücke zwischen Container- und Heap-Limit kleiner ausfallen, ohne dass der Container mit OOMKilled beendet wird.

webtoolkit:
tileCompressionType: IMAGEIO

Alle Werte des Tile-Formats beschreibt Application Properties.

Verbindungstyp​

Nutzen Sie nach Möglichkeit WebSocket. Server Sent Events und Longpoll sind Ausweichlösungen, wenn WebSocket in der Infrastruktur (Proxy, Firewall) nicht durchgängig möglich ist. Siehe Connection API.

Packaging​

Bauen Sie eine Spring-Boot-Anwendung als JAR, nicht als WAR. Das WAR-Packaging ist nur für das Traditional Deployment auf einem Application Server gedacht. Hintergrund: Beim Lesen von PDF-Dokumenten werden Klassen dynamisch über den Classpath gesucht, und dieser Classpath-Scan ist im WAR-Packaging von Spring Boot deutlich teurer.

Thread-Pools und Preloading​

  • Thread-Pools sind anhand der Prozessorzahl sinnvoll voreingestellt. Anpassungen sind selten nötig und wirken sich auf das Gesamtsystem aus. Die Properties beschreibt Application Properties.
  • Preloading beschleunigt die Anzeige benachbarter Seiten. Empfohlen ist ein Bereich von 0–3 Seiten, siehe Tile-Rendering.

Serverseitiger Cache​

Der Browser-Cache ist immer aktiv. Serverseitig werden Dokument-Strukturinformationen und Zwischenergebnisse gecacht; das Caching gerenderter Tiles ist standardmäßig ausgeschaltet (webtoolkit.tileCachingEnabled: false) und sollte es in der Regel auch bleiben.

Richtwerte​

Property (jadice.viewer.cache.…)EmpfehlungHinweis
maxNumberOfCacheEntries90000Siehe Berechnung unten. Eher höher wählen, damit der Speicher und nicht die Anzahl der Einträge begrenzt.
sizeHighwaterMarkPercent25Anteil am maximalen Heap; bei 8 GB Heap entspricht das 2 GB.
sizeHighwaterMarknicht setzenStattdessen sizeHighwaterMarkPercent verwenden.
minimumExpiryAge60000Mindestverweildauer eines Eintrags in Millisekunden.
maximumExpiryObjectCountRatio10Angabe in Prozent.
maxAgeExpiryEnabledtrueEntfernt Einträge, deren letzter Zugriff länger als maxAge zurückliegt.
maxAge / maxAge.timeUnit60 / MINUTES

Wie diese Werte gesetzt werden, beschreibt Dimensionierung der Hardware und Caching.

Berechnung der Cache-Größe​

Die Richtwerte beruhen auf folgender Abschätzung, die mit der Enterprise Demo und verschiedenen Dokumenten empirisch ermittelt wurde:

  • Je Dokument belegt der Cache etwa 2,5–4,5 MB (Dokument, Seitensegmente, Strukturinformationen) und 150–300 Einträge.
  • Annahme: Im Mittel werden 10 Dokumente pro Minute geladen, und ein Dokument soll etwa 30 Minuten im Cache bleiben.
  • Speicher: 10 × 30 × 2,5 MB = 750 MB bis 10 × 30 × 4,5 MB = 1350 MB
  • Einträge: 10 × 30 × 300 = 90 000

Setzen Sie für Ihre Installation die eigene Ladefrequenz und gewünschte Verweildauer ein.

Cache als Engpass​

Werden Dokumente zu häufig aus dem Cache verdrängt und neu geladen, obwohl der Cache nur gering belegt ist, kann der Wechsel auf einen LRUCache helfen. Den Cache setzen Sie im Einstiegspunkt der Integration:

LRUCache cache = new LRUCache(120000);
cache.setSizeHighwaterMark(1000 * 1024 * 1024);
CacheManager.setDefault(cache);

JVM-Parameter​

Empfohlener Parametersatz für Spring Boot im Container mit 8 GB RAM und JDK 25:

-XX:+UseG1GC
-XX:InitialRAMPercentage=75.0
-XX:MaxRAMPercentage=75.0
-XX:MaxMetaspaceSize=384m
-XX:G1HeapRegionSize=16m
-XX:+UseCompactObjectHeaders
-XX:+UseStringDeduplication
-XX:+ExitOnOutOfMemoryError
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/dumps/heapdump.hprof
-Xlog:gc*:file=/var/log/jvm/gc.log:time,uptime,level,tags:filecount=5,filesize=10M
note

-XX:+UseCompactObjectHeaders ist erst ab JDK 25 ein reguläres Feature. Unter älteren JDKs entfällt der Parameter; die JVM startet sonst nicht.

Heap​

InitialRAMPercentage und MaxRAMPercentage auf denselben Wert zu setzen fixiert den Heap und verhindert Resizing im Betrieb. 75 % von 8 GB ergeben etwa 6 GB Heap.

Mehr als 75 % sind ohne Messung nicht zu empfehlen. Außerhalb des Heaps liegen Metaspace, Code Cache, Thread-Stacks, GC-Metadaten und NIO-Puffer – erfahrungsgemäß zusammen 1–1,5 GB. Reicht dieser Rest nicht, beendet die Container-Laufzeit den Prozess mit OOMKilled, statt dass die JVM einen OutOfMemoryError wirft. Dann entsteht auch kein Heap-Dump.

Speicherlimit setzen

MaxRAMPercentage bezieht sich auf das Speicherlimit des Containers. Ohne resources.limits.memory im Kubernetes-Manifest liest die JVM den RAM der gesamten Node: Auf einer 32-GB-Node werden aus 6 GB Heap dann 24 GB.

BereichGröße (8 GB Container)
Heapca. 6 GB (75 %)
Metaspace (Obergrenze)384 MB
Code Cache, Thread-Stacks, GC-Metadaten, NIO-Pufferca. 1–1,5 GB
ReserveRest

Die Werte sind Richtwerte. Belastbar wird die Aufteilung durch eine Messung mit Native Memory Tracking am eigenen Dokumentenbestand: -XX:NativeMemoryTracking=summary temporär setzen (ca. 5 % Overhead) und mit jcmd <pid> VM.native_memory auswerten.

Region Size​

-XX:G1HeapRegionSize=16m ist für das jadice web toolkit der wichtigste GC-Parameter. G1 behandelt Objekte ab der halben Regionsgröße als Humongous und legt sie direkt in der Old Generation ab. Gerasterte Seiten liegen als große Arrays im Heap und überschreiten den Default-Schwellwert regelmäßig. Mit 16 MB Regionen liegt die Schwelle bei 8 MB.

Off-Heap-Speicher​

Der Parametersatz enthält bewusst kein -XX:MaxDirectMemorySize. Mit PNG als Tile-Format wird off-heap nur wenig Speicher benötigt. Wird dennoch WebP verwendet, setzen Sie -XX:MaxDirectMemorySize explizit und senken den Heap-Anteil entsprechend, damit das Container-Limit nicht unbemerkt überschritten wird.

Heap-Dumps und GC-Log​

HeapDumpOnOutOfMemoryError und ExitOnOutOfMemoryError ergänzen sich: Der Dump wird geschrieben, danach beendet sich die JVM und die Orchestrierung startet den Container neu.

Nicht nach /tmp schreiben

In Kubernetes ist /tmp je nach Manifest ein emptyDir mit medium: Memory (tmpfs). Ein Heap-Dump landet dann im RAM und zählt gegen dasselbe Speicherlimit. Verwenden Sie ein Volume mit echtem Storage und mindestens Heap-Größe freiem Platz. Ohne ein solches Volume schalten Sie HeapDumpOnOutOfMemoryError ab.

Heap-Dumps enthalten Dokumentinhalte und gegebenenfalls personenbezogene Daten. Schützen Sie das Volume entsprechend und löschen Sie Dumps nach der Analyse.

Nicht mehr benötigte Parameter​

Folgende Parameter finden sich häufig in älteren Konfigurationen. Unter JDK 25 entsprechen sie dem Default oder sind wirkungslos und können entfallen:

ParameterGrund
-XX:MaxGCPauseMillis=200Default in G1.
-XX:G1ReservePercent=10Default.
-XX:InitiatingHeapOccupancyPercent=45Nur Startwert; G1 passt den Wert im Betrieb adaptiv an.
-XX:MinRAMPercentage=75.0Wirkt nur bei Containern unter ca. 250 MB RAM.
-XX:MetaspaceSize=256mSetzt nur die Schwelle für die erste Metaspace-GC.
-XX:+UseContainerSupportDefault seit JDK 10.
-XX:+UseCompressedOops, -XX:+UseCompressedClassPointersDefault bei Heaps unter 32 GB.
-XX:+OptimizeStringConcatObsolet seit JDK 9.
-Djava.security.egd=file:/dev/./urandomObsolet seit JDK 9.
-XX:+UnlockExperimentalVMOptionsFür UseCompactObjectHeaders ab JDK 25 nicht mehr nötig.
-XX:+AlwaysPreTouchVerlängert den Start deutlich; nur bei gemessenen Latenzspitzen in der Warmlaufphase sinnvoll.

Parameter gegen das Basis-Image prüfen​

Ein unbekanntes Flag in JAVA_TOOL_OPTIONS verhindert den Start der JVM; der Pod landet in CrashLoopBackOff. Prüfen Sie den Parametersatz deshalb in der CI gegen das tatsächlich verwendete Basis-Image:

docker run --rm <basisimage> -XX:+UseCompactObjectHeaders -version

Pinnen Sie das Basis-Image im Dockerfile per Digest statt per Tag. Das macht Builds und die SBOM reproduzierbar, was auch für den Nachweis der Komponentenherkunft nach dem Cyber Resilience Act relevant ist.

Weitere Einstellungen​

  • Tolerantes Lesen von PDF: Je nach Dokumentenbestand kann der Lesemodus LENIENT_ON_ERROR sinnvoll sein, damit PDFs mit kleineren Syntaxfehlern trotzdem angezeigt werden. Gesetzt wird er im DocumentDataProvider direkt vor reader.read(...):
    PDFStructureReaderSettings settings = reader.getSettings(PDFStructureReaderSettings.class);
    settings.setPDFStructureReadStrategy(PDFStructureReadStrategy.LENIENT_ON_ERROR);
    Risiken beschreibt die Dokumentation der jadice document platform unter PDF-Struktur Lesestrategien.
  • Eingebettete MO:DCA-Annotationen in AFP-Dokumenten anzeigen: jadice.viewer.afp-show-inline-annotation=true

Weiterführende Informationen​