A 24-hour JMeter soak test doesn’t actually detect memory leaks; it provides the opportunity for a leak to manifest and be observed through other monitoring tools.

Let’s get JMeter running for the long haul. The goal here is to simulate realistic, sustained load for an extended period, typically 24 hours or more, to uncover issues that only appear under prolonged stress, with memory leaks being a prime suspect.

First, you need a JMeter test plan that reflects your production traffic. This means realistic user counts, think times, pacing, and a variety of requests that mimic what your application experiences daily. For a soak test, we’re not looking for peak performance, but stability.

Here’s a sample jmeter.properties snippet you’ll want to tune for a long run. You don’t need to set everything, just what’s relevant to prevent JMeter itself from becoming the bottleneck or misreporting.

# Set to false to disable saving of test plan in XML format on shutdown.
# This prevents JMeter from writing a potentially large file after a long test.
jmeter.save.saveservice.output_format=csv
jmeter.save.saveservice.assertion_results_failure_messages=false
jmeter.save.saveservice.assertion_results_all_messages=false
jmeter.save.saveservice.response_data=false
jmeter.save.saveservice.samplerData=false
jmeter.save.saveservice.request_data=false
jmeter.save.saveservice.landing_page=false
jmeter.save.saveservice.response_headers=false
jmeter.save.saveservice.request_headers=false
jmeter.save.saveservice.latency=true
jmeter.save.saveservice.connect_time=true
jmeter.save.saveservice.samplerData=false
jmeter.save.saveservice.serverTimers=false
jmeter.save.saveservice.busy_sleep_interval=100
jmeter.gui.demo_mode=false
# Increase heap size for the JMeter JVM if running a very large test plan
# or many threads directly from JMeter GUI (not recommended for soak tests).
# For command-line execution, this is usually handled by the startup script.
# Example: HEAP="-Xms1024m -Xmx4096m" # Set initial and max heap sizes

The most critical part of a soak test isn’t JMeter itself, but what you’re monitoring around JMeter and your application. For memory leaks, you’ll need to track the JVM heap usage of your application servers. Tools like VisualVM, YourKit, or even basic jstat commands are your best friends here.

Let’s say you’re running your application on a Linux server with your application’s JVM. You’d typically execute JMeter from a separate machine or a dedicated load generator. On your application server, you’d run something like this periodically:

# Get PID of your application's Java process
APP_PID=$(pgrep -f "your.application.MainClass")

# Monitor heap usage (Eden, Old Gen, Heap Total) every 5 seconds
jstat -gc $APP_PID 5000 > app_heap_usage.log

This command, jstat -gc <pid> <interval>, dumps garbage collection statistics, including heap generation sizes, every 5000 milliseconds (5 seconds). Over 24 hours, this log file will show you if the heap is steadily increasing without a corresponding increase in garbage collection activity that reclaims memory.

The tell-tale sign of a memory leak in the jstat output is a consistent, upward trend in the "Old Gen" or "Full GC" memory usage over time. Even after full garbage collections, the old generation heap size doesn’t return to a baseline, indicating that objects are being retained longer than they should.

For a JMeter soak test, you’d launch JMeter in non-GUI mode from your load generator:

jmeter -n -t /path/to/your/testplan.jmx -l /path/to/results/results.csv -e -o /path/to/dashboard/report
  • -n: Non-GUI mode (essential for stability and performance).
  • -t: Path to your JMeter test plan (.jmx file).
  • -l: Path to save the raw test results (.csv file).
  • -e: Generate HTML report at the end of the test.
  • -o: Folder to output the HTML report.

The HTML dashboard generated by JMeter (-e -o) is useful for overall performance trends but won’t directly show memory leaks. You’ll correlate its data (response times, error rates) with your external application JVM monitoring.

If you see a steady increase in your application’s heap usage over the 24 hours, coupled with potentially increasing response times or a higher rate of full garbage collections in your jstat logs, you’ve likely found a memory leak. The next step is to use a profiler (like YourKit, JProfiler, or VisualVM with its sampling profiler enabled) attached to your running application to pinpoint the exact objects being retained and the code paths responsible for holding onto them.

The most common cause of memory leaks in Java applications is holding references to objects longer than necessary, often within static collections, listeners that aren’t unregistered, or caches that don’t have proper eviction policies.

Another common leak source is thread-locals that are not cleaned up. If a thread lives for a long time and accumulates thread-local data without clearing it, that data can persist.

Caches, especially unbounded ones, are notorious. If you have a HashMap or similar structure used as a cache and never remove entries, it will grow indefinitely.

Unclosed resources, while often leading to other resource exhaustion (like file handles), can sometimes indirectly contribute to memory leaks if the objects holding those resources are also holding onto significant amounts of data.

Listeners or observers that are added but never removed from a subject can cause leaks, as the subject will keep references to them.

Improperly managed thread pools can also be a source of leaks if threads are kept alive unnecessarily or if their associated data isn’t managed.

Finally, native memory leaks (outside the JVM heap, but still consuming system memory) can occur if your application uses JNI and doesn’t properly manage native resources.

After your 24-hour soak test, you’ll be looking at your application’s JVM heap usage trends and JMeter’s performance metrics. The next thing to investigate will likely be CPU usage spikes, as excessive garbage collection cycles caused by memory pressure can heavily impact CPU.

Want structured learning?

Take the full Jmeter course →