AEM performance tuning for JVM heap and garbage collection
Performance work on Adobe Experience Manager (AEM) is often framed as a heap-sizing exercise: choose an Xmx value, wait longer for garbage collection and hope response times recover. That misses the full picture. AEM instances combine Oak repository work, asset processing, Sightly rendering, search indexes, caches, connectors and application code, so heap pressure may be only one source of delay. Effective AEM performance tuning connects JVM memory behaviour to the requests and publishing operations that matter.
The approach matters in Australia, where AEM services may run in Sydney or Melbourne while audiences browse from Perth, Adelaide, regional offices and mine sites. Hosting on the east coast can hide poor performance for western users if teams test only from Melbourne. Intermittent links can magnify retries and long-lived requests. A retail campaign before EOFY, a government service announcement or a morning release can create sharp traffic peaks. The goal is predictable latency under a realistic workload, not the largest possible heap.
| Collector | Best fit | Operational strength | Risk to watch |
|---|---|---|---|
| G1 | General AEM workloads on supported modern JDKs | Balanced throughput and pause control | Occupancy-driven collections and promotion churn |
| Parallel | Publishing and batch-processing windows | High throughput and efficient CPU use | Longer pauses during latency-sensitive traffic |
| ZGC | Large heaps on an approved JDK 15 or later stack | Very short collection pauses | Additional CPU and memory overhead |
| Shenandoah | Low-pause workloads with a supported JDK vendor | Predictable pauses independent of heap size | Distribution and AEM support vary |
| CMS | Legacy Java 8 estates only | Fine-grained control of young and old collections | Removed from modern JDKs; outdated flags create risk |
Size the heap from measured demand
Start with a representative publishing and delivery workload, not a synthetic heap benchmark. Record committed and used heap, resident memory, allocation rate, garbage collection time and request latency at idle, normal load and peak load. Oak metadata, indexes, cached assets and application objects use the heap, while BlobStore binaries normally remain outside it.
Define a steady-state working set plus burst headroom, then allow for metaspace, code cache, direct buffers, thread stacks and native libraries. A 25 to 35 per cent non-heap allowance may be a starting hypothesis, but load tests must validate it. Set Xms near stable demand and Xmx as a tested ceiling, not automatically the container limit. Confirm that the AEM-supported Java release, container runtime and operating system agree on the memory model.
Match the collector to service goals
The safest collector is usually the default for a supported AEM release because vendor qualification and tooling matter. G1 is a sound baseline on modern JDKs: it collects older generations concurrently and offers useful pause controls. Parallel collection can suit offline publishing and batch windows where throughput matters more than tail latency. Low-pause collectors need extra scrutiny because their CPU and memory costs may outweigh the benefit.
Legacy CMS settings should not be copied forward. CMS was still available in Java 8 when CIRCUIT covered the 2015 and 2016 events, but it was deprecated and later removed. ZGC became production-ready in JDK 15, while Shenandoah depends on the JDK distribution and AEM support matrix. Benchmark representative traffic and compare pauses, throughput, CPU cost and recovery after full collections; fewer flags often create fewer operational surprises.
Read the garbage collection record
Enable garbage collection logging and keep it across releases. On Java 11 and later, a unified logging option such as -Xlog:gc*,safepoint:file=/var/log/aem/gc.log:time,uptime,level,tags:filecount=5,filesize=20M provides rotation and consistent timestamps. Java 8 installations can use -XX:+PrintGCDetails with -Xloggc. Confirm file permissions and retention so an incident does not lose its first minutes of evidence.
Read trends, not isolated pauses. Look for allocation rate, promotion, occupancy after collection, pause percentiles and time spent collecting. Rising occupancy after full collections suggests retained data; frequent short collections can indicate allocation churn. Correlate these events with CPU, disk latency, lock contention and request traces, since a slow request is not automatically a collection pause. Capture heap dumps only under controlled conditions because they can pause the process and consume substantial disk.
Account for memory outside the heap
The collector can only reclaim managed objects. Metaspace, the code cache, direct buffers, thread stacks and native libraries sit outside the Java heap, and AEM integrations may add direct-buffer pressure through network clients. Use -XX:MaxMetaspaceSize, -XX:ReservedCodeCacheSize and -XX:MaxDirectMemorySize as guardrails where appropriate, rather than arbitrary limits. An explicit direct-memory cap also prevents an integration from consuming the space reserved for the heap.
Compressed ordinary object pointers improve memory density for heaps up to 32 GB on supported JDKs; moving beyond that threshold can increase heap consumption. Prefer an Xmx at or below 32 GB when the workload permits, then verify the object footprint in a heap dump. Also check container detection and MaxRAMPercentage, because explicit heap flags can override ergonomic defaults. Native thread counts and file-descriptor limits deserve the same attention: large thread pools can exhaust memory even when the heap looks healthy.
Protect tail latency during local peaks
Australian traffic is not uniformly coastal. A launch can concentrate Sydney and Melbourne requests in the same morning, while Perth, Adelaide and regional users encounter greater network delay. Put cacheable assets behind a CDN or well-tuned Dispatcher cache, and keep personalised repository work at the origin. Australian data-residency requirements may also prevent one global region from serving every workload. Test a campaign before EOFY or a government announcement as a sudden concurrency spike rather than a gentle ramp.
Mobile clients widen the latency budget. The CIRCUIT material on mobile PhoneGap apps illustrates how application delivery extends beyond a desktop browser. Australian field crews, mine operators and retail staff often use bring-your-own-device phones over variable connections. Compress images, cap rendition dimensions, use asynchronous work where possible and avoid making every mobile request wait on a synchronous integration.
Heap tuning cannot repair every AEM bottleneck. Oak compaction, search indexing, rendition generation and slow connectors compete for the same CPU and disk I/O as garbage collection. Schedule intensive publishing work carefully, but do not hide contention behind a larger heap. Measure end-to-end p95 and p99 latency alongside collector pauses so the team improves the journey rather than merely reducing one JVM metric.
Use a controlled tuning loop
Run tuning as a controlled loop: establish a baseline, change one variable, apply a representative load, compare results and retain or revert the change. Synthetic journeys should include DAM searches, asset renditions, authored pages and integration calls, not just the home page. Record throughput, errors, CPU, heap occupancy, collection pauses and request percentiles in the same run.
Use a sensible order of intervention. Fix inefficient queries, unbounded caches or synchronous integration before enlarging the heap. Then test collector flags, heap ceilings and allocation hotspots against the same workload. Canary a change on one deployment, compare it with the control and keep a tested rollback command ready. Heap and collector tuning should improve service levels without moving risk elsewhere.
Document the result in an operational runbook that travels between Sydney, Melbourne and offshore support teams. Record the JDK build, AEM version, JVM arguments, container limits, baseline measurements and the reason for every change. This turns a local optimisation into a repeatable practice rather than tribal knowledge, particularly when staff cover Australian business hours from different locations.
Join the AEM developer community
CIRCUIT brings Java developers, AEM architects, front-end engineers and systems specialists together for technical sessions and practical exchange. The archived 2015 and 2016 recordings are useful references for JVM and AEM architecture discussions. For a new attendance, check Chicago’s schedule against Sydney and Melbourne time, then review the available programme and passes through event registration.
Reserve a place and bring a sanitised garbage collection log, a representative workload profile and one measurable tuning hypothesis. That evidence turns a broad performance conversation into a concrete engineering review for Australian teams.