skip to content

What happens to a JMeter non-GUI run when a sampler thread throws OutOfMemoryError?

level: seniorimportance: should knowfreq 48%

answer

  1. One catch clause does not match
  2. The handler is global, not per element
  3. Look for ERROR in the run log
  4. The launcher already asked for a dump

basics

~10 s

The thread dies and the run carries on. JMeter's default uncaught-exception handler logs an ERROR block in jmeter.log and prints one line to stderr, and bin/jmeter's always-on heap-dump flag leaves a .hprof file behind.

solid answer

~40 s

`JMeterThread.run()` catches the stop-test exceptions, `Exception | JMeterError` and `ThreadDeath`; `OutOfMemoryError` matches none of those, so it unwinds out of the thread. The `finally` block still runs, so the thread is de-registered and the next `+` line shows `Active` down by one and `Finished` up by one. The error then reaches the default uncaught-exception handler JMeter installs at startup, which writes an `ERROR` entry to `jmeter.log` reading `Uncaught exception in thread` plus the stack trace, and prints `Uncaught Exception ... See log file for details.` on standard error. Because `bin/jmeter` always passes `-XX:+HeapDumpOnOutOfMemoryError`, the JVM also writes a `.hprof` into the working directory. **The test itself is not stopped.**

code

text · 5 lines
text
2026-03-04 02:41:19,884 ERROR o.a.j.JMeter: Uncaught exception in thread Thread[#94,Checkout 1-871,5,main]
java.lang.OutOfMemoryError: Java heap space
	at java.base/java.util.Arrays.copyOf(Arrays.java:3745)
	at java.base/java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:172)
	... 21 more

go deeper

for a junior

Know that jmeter.log is a separate file from the results file, and that errors from inside the engine land there rather than in the JTL rows.

for a middle

Explain which catch clauses JMeterThread has and why an OutOfMemoryError misses all of them, ending up at the default uncaught-exception handler instead of in a per-thread log line.

for a senior

Build the habit of grepping jmeter.log for ERROR after every run and lining its timestamps up against the summariser's, because a dead thread changes the run's meaning without changing anything you can see on the console.

for a principal

Own the artefact policy: where heap dumps are allowed to land, how much disk that needs, whether every run gets its own log file, and what the pipeline archives before it reaps the workspace.

## The error escapes the thread's own handling `JMeterThread.run()` wraps its work in a set of catch clauses: the stop-test exceptions, then `Exception | JMeterError`, then `ThreadDeath`. `JMeterError` is JMeter's own `Error` subclass and the only `Error` that clause will match; the one other `Error` in the list, `ThreadDeath`, is caught solely to be rethrown. **`OutOfMemoryError` matches neither**, so it does not become a logged `Test failed!` line the way an ordinary sampler exception does — it keeps unwinding. The thread's `finally` block still runs on the way out. That block calls the thread-finished listeners, decrements the engine's active-thread counter and removes the thread's context, so the thread is properly de-registered: **`Active` falls by one and `Finished` rises by one on the next summariser `+` line**, exactly as it would for a thread that ended normally. ## Where it surfaces JMeter installs a default uncaught-exception handler at startup. Once the error leaves `run()`, that handler produces two things: - an `ERROR` entry in `jmeter.log`, reading `Uncaught exception in thread` followed by the thread's name and the full stack trace; - a line on **standard error**: `Uncaught Exception ... in thread ... . See log file for details.` Note that stderr line. If your pipeline captures stdout and discards stderr, the console record of the run keeps the summariser lines and loses the only in-band warning that something died. ## The heap dump you already asked for `bin/jmeter` sets `DUMP="-XX:+HeapDumpOnOutOfMemoryError"` unconditionally, with the comment that it costs nothing unless triggered, and passes it to the JVM alongside the heap and GC settings. So an injector that runs out of heap **writes a `.hprof` file into its working directory** without anyone having asked for it at run time. That is worth planning for rather than discovering: - the dump is roughly the size of the live heap, so a `-Xmx4g` injector can drop several gigabytes onto whatever volume the run's working directory sits on; - a CI workspace that is wiped after the job takes the dump with it; - on a distributed run, the dump lands on the machine whose JVM died, not on the controller. ## Why nothing announces it Put the three behaviours together and the shape of the failure is clear. The engine does not stop. The surviving threads keep sampling, the summariser keeps printing `+` lines, and the results file keeps growing. The only in-band evidence is one `ERROR` block in `jmeter.log`, one line on stderr, and a heap dump nobody was looking for. Two habits follow from that: 1. **Grep the log, do not skim the console.** `grep -c ERROR jmeter.log` after every run is cheaper than reading the scrollback, and the log carries timestamps you can line up against the summariser's. 2. **Keep the log.** The `File` appender in `bin/log4j2.xml` opens `jmeter.log` with `append="false"`, so the next run on that machine truncates it. Give each run its own file with `-j` or archive it before the next one starts.

  • Why does an OutOfMemoryError not produce the usual 'Test failed!' log line?
    That line comes from `JMeterThread`'s `catch (Exception | JMeterError e)` clause. `JMeterError` is JMeter's own `Error` subclass and the clause matches nothing else, so an `OutOfMemoryError` walks straight past it and is only picked up further out by the default uncaught-exception handler.
  • Where does the heap dump go, and what should you do about it in CI?
    Into the JVM's working directory, which for a pipeline is usually the job workspace that gets wiped afterwards. Its size tracks the live heap, so decide in advance whether the volume can take it and whether the job archives `*.hprof` before cleanup.
  • Why can last night's jmeter.log be gone by the time you look for it?
    The `File` appender in the shipped `bin/log4j2.xml` opens `jmeter.log` with `append="false"`, so each start truncates it. The rolling appender in that file is commented out. Give each run its own log with `-j`, or archive the file before the next run begins.

saying these in an interview costs you the question

  • Assumes an injector OOM stops the test immediately
  • Expects the error to appear in the JTL as a failed sample
  • Says a heap dump only appears if you add the flag yourself
  • Looks only at the console and never greps jmeter.log
  • Believes JMeter rolls jmeter.log rather than truncating it