Jenkins Error: 'java.lang.OutOfMemoryError: Java heap space' — Cause, Fix, and Troubleshooting Guide
Fix 'java.lang.OutOfMemoryError: Java heap space' in Jenkins — raise -Xmx, tune GC, cut executors, and stop huge logs and artifacts exhausting the heap.
- #automation
- #troubleshooting
- #jenkins
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
java.lang.OutOfMemoryError: Java heap space means the Jenkins JVM tried to allocate an object but the heap was already full and garbage collection could not free enough space. The controller (or an agent’s JVM) then throws, and depending on where the allocation happened a build fails, a thread dies, or the whole Jenkins process becomes unresponsive and eventually needs a restart.
In a build console you will see it thrown from whatever code was allocating at the time:
[Pipeline] sh
+ mvn -B package
java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.Arrays.copyOf(Arrays.java:3720)
at hudson.model.Run.getLogText(Run.java:1478)
at org.jenkinsci.plugins.workflow.job.WorkflowRun...
Build step 'Execute shell' marked build as failure
A closely related variant appears when the JVM is spending almost all its time collecting garbage and reclaiming very little — it gives up rather than thrashing forever:
java.lang.OutOfMemoryError: GC overhead limit exceeded
Both are heap-exhaustion errors. When the controller itself hits this, the UI stops responding, agents disconnect, and /var/log/jenkins/jenkins.log fills with the same stack repeated across threads.
Symptoms
- Builds fail intermittently with
OutOfMemoryError: Java heap space, often the larger or longer ones. - The Jenkins UI hangs, returns 502/503, or agents drop with
channel is already closedright before or after the OOM. jenkins.logshows the OOM stack repeated on many threads within seconds.- Memory climbs steadily over days and only a restart recovers it (a leak signature).
GC overhead limit exceededappears under heavy concurrent builds.
sudo grep -c 'OutOfMemoryError' /var/log/jenkins/jenkins.log
47
Common Root Causes
1. -Xmx too low for the job volume and plugin set
The default or historical heap size no longer fits the number of concurrent builds, installed plugins, and cached build records the controller holds in memory.
# What heap is the running JVM actually configured with?
ps -ww -o args= -p "$(pgrep -f 'jenkins.war')" | tr ' ' '\n' | grep -E '^-Xm'
-Xmx512m
512 MB is far too small for a busy controller with many plugins.
2. A memory leak in a plugin
A plugin retains references to build data, so heap grows monotonically until it exhausts, regardless of -Xmx. Only a restart resets it, and it fills again.
3. Large build logs or artifacts loaded into memory
Reading an entire multi-hundred-megabyte console log or archiving huge artifacts in one buffer allocates a single enormous array — a classic copyOf OOM.
4. Too many executors for the available heap
Each concurrent build carries live objects. Setting the controller to run many executors multiplies peak heap and pushes it past -Xmx under load.
5. Large XML parsing / config reload
Parsing very large config.xml, job configs, or test result XML (JUnit) into DOM trees spikes heap during load or at build post-processing.
6. Agent JVM under-provisioned
The OutOfMemoryError originates on an agent whose remoting JVM was launched with a small heap, not the controller.
How to Diagnose
First confirm where the OOM is thrown — controller or agent — and how heap is currently sized:
sudo tail -n 100 /var/log/jenkins/jenkins.log | grep -A3 OutOfMemoryError
ps -ww -o pid,rss,args= -p "$(pgrep -f jenkins.war)"
1934 498210 /usr/bin/java -Xmx512m -jar /usr/share/java/jenkins.war ...
rss in KB shows real memory use; the -Xmx value shows the ceiling. If rss is pinned near the ceiling, the heap is simply too small or leaking.
Watch GC and heap occupancy live with jstat — sustained high old-generation usage that never drops after a full GC points at a leak, not just an undersized heap:
sudo -u jenkins jstat -gcutil "$(pgrep -f jenkins.war)" 2000 5
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 12.34 88.71 99.02 95.10 92.44 418 22.104 61 48.900 71.004
O (old gen) at 99% with frequent full GCs (FGC) climbing is the leak/over-pressure signature.
Capture a heap dump for offline analysis. Configure the JVM to dump automatically on the next OOM so you catch the real culprit:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/jenkins/heapdumps
Or take one on demand and inspect the largest object histogram:
sudo -u jenkins jmap -histo:live "$(pgrep -f jenkins.war)" | head -15
num #instances #bytes class name
1: 4821330 231423840 [B
2: 980221 47050608 hudson.model.Run$...
3: 612004 29376192 java.lang.String
A single plugin or Run class dominating the histogram tells you which subsystem is holding memory. The bundled Monitoring plugin (JavaMelody) exposes the same heap/GC trends in the UI if you prefer not to use CLI tools.
Fixes
Raise the heap ceiling
Set -Xmx (and a matching -Xms to avoid resizing pauses) where your platform reads JVM options. On Debian/Ubuntu with the systemd unit, use an override:
sudo systemctl edit jenkins
[Service]
Environment="JAVA_OPTS=-Xms2g -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/jenkins/heapdumps"
sudo systemctl daemon-reload
sudo systemctl restart jenkins
On RHEL-family packages the value may live in /etc/sysconfig/jenkins (JENKINS_JAVA_OPTIONS); for the Windows service it is in jenkins.xml. Size -Xmx to leave headroom for the OS and non-heap memory — do not set it to the full machine RAM.
Tune GC
Modern JDKs default to G1, which handles large heaps well. Prefer raising the heap over disabling the GC overhead check. If you were using an old parallel/CMS collector, moving to G1 usually removes GC overhead limit exceeded:
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
Reduce executors and split jobs
Lower the controller’s executor count (ideally to 0 on the controller, running builds only on agents) so peak heap is bounded, and break monolithic pipelines into smaller ones:
Manage Jenkins -> Nodes -> (built-in node) -> # of executors: 0
Offload logs and artifacts
Stop streaming giant logs and artifacts through the JVM heap. Archive to external storage, cap console output, and discard old builds so the controller does not retain them:
// In a declarative pipeline
options {
buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '10'))
}
Fix the leaking plugin
If the heap dump implicates a plugin, update or remove it. Track heap trend after each change with jstat; a healthy controller returns old-gen occupancy to a low baseline after a full GC.
What to Watch Out For
- Raising
-Xmxmasks a leak — it does not fix one. If old-gen occupancy never falls after a full GC, more heap just delays the OOM. Capture a heap dump and find the retaining class before you keep bumping the ceiling. -Xmxis not total process memory. Metaspace, thread stacks, and native buffers live outside the heap; set-Xmxbelow physical RAM or the OOM killer may reap the JVM even though the heap fit.- The build console OOM may be an agent, not the controller. Check whether the stack came from
jenkins.log(controller) or the agent’s remoting log before you resize the wrong JVM. - Restarts hide the trend. A leak that fills the heap over days looks like a random OOM if Jenkins is restarted for other reasons. Graph heap over time (Monitoring plugin) to see the slope.
GC overhead limit exceededis still an OOM. Do not disable it with-XX:-UseGCOverheadLimit; that just converts an early, clean failure into slow thrashing followed byJava heap space.
Related Guides
- systemd Timer Failed with Exit Code
- Scheduled Job Orchestration at Scale
- GitHub Actions Reusable Workflows for Automation at Scale
Fixed it? Get 500 Automation & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.