Chapter 26Lesson 01~185 minutes

Load Generator Sizing, JVM Tuning, OS Limits, and Network Capacity: Core Concepts and Mental Model

Chapter 25 proved that distributed load is the sum of multiple engines. That immediately creates the next engineering question: how much valid load can one engine actually sustain? A target can appear slow simply because JMeter is at 100% CPU, pausing for GC, churning TCP ports, waiting on DNS, blocking on disk writes or saturating its network interface. Generator health is therefore part of the experiment—not an implementation detail hidden behind the JMX.

Generator sizingHeap/GCCPUSockets/portsNetwork/JTL

Learning objectives

  • Model configured workload as demand on finite generator resources.
  • Define heap, GC, CPU saturation, file descriptors/handles, sockets, ephemeral ports, DNS cache, disk I/O and network throughput.
  • Distinguish client elapsed time from target service time and timer delay.
  • Inspect generator state before tuning it.
  • Reject universal “threads per core/GB” sizing rules.
  • Define usable generator capacity from achieved workload plus resource headroom.

1. The practical problem: the injector can saturate first

Suppose a plan is configured for 2,000 virtual users. The target reports low CPU and stable service time, but JMeter achieved RPS falls while the generator reaches 100% CPU and GC time rises. Declaring “the server saturated at this load” would be wrong: the injector failed to deliver the experiment.

Mandatory chapter boundary: all executable work is local and disposable. Target = 127.0.0.1:8026. Normal profile = 10 threads ×100 loops = 1,000 samples, 100 ms pacing, 8 KiB synthetic response, ≤20 seconds/run. No OS limits, firewall/antivirus, DNS/JDK security or production settings are changed; the lab uses no real credentials. Never substitute a public/shared/production endpoint.

2. Mental model: plan cost + concurrency → generator resources → achieved load

Generator capacity and validity flow

A JMeter sample is generated by an injector that has finite CPU, heap, sockets, resolver, disk and network capacity. If one of those resources saturates, the configured workload and the achieved workload diverge even when the target itself is healthy.

flowchart TD
P[Test-plan cost
threads / timers / assertions / scripts / listeners] --> J[JMeter JVM]
J --> H[Heap allocation + GC]
J --> C[CPU scheduling]
J --> S[Sockets / OS handles or FDs / local ports]
J --> D[DNS resolution / caches]
J --> N[Network interface + TCP]
J --> R[Result serialization + JTL disk I/O]
S --> T[HTTP target]
D --> T
N --> T
T --> E[Response SampleResults]
E --> J
J --> A[Achieved samples / RPS / latency]
G[Generator telemetry] --> V[Validity decision]
A --> V
T --> ST[Target service telemetry]
ST --> V

The plan determines how many threads exist and how much work each completed sample performs inside the injector. JMeter allocates Java objects, schedules work on CPU, opens/reuses sockets, may resolve names, transfers request/response bytes and serializes selected result fields. The target responds, but the measured SampleResult includes the entire client-visible path. Generator telemetry and target service telemetry therefore have to be evaluated together before a latency/capacity conclusion is valid.

3. Core generator terms

Term Beginner definition What failure looks like
Heap Java-managed memory used by JMeter objects, samples, scripts, protocol state and listeners. Old-gen/heap pressure, frequent/long GC, OOM, allocation stalls.
Garbage collection (GC) JVM reclaims unreachable objects. Rising GC time/pauses and less CPU time available for sampling.
CPU saturation Generator cores have little scheduling headroom. High process/system CPU, delayed timers/samplers, achieved RPS plateaus.
Handle / file descriptor OS reference to files/sockets/etc.; Windows reports handles, POSIX systems expose FDs. Open failures, socket/file errors, rising handle/FD count near a system/process limit.
Socket One endpoint of a network connection. Large established/TIME_WAIT sets, connect errors, local-port pressure.
Ephemeral/local port Temporary local TCP source port used for outgoing connections. Rapid connection churn consumes port space/TIME_WAIT state.
DNS Hostname-to-address resolution path and caches. Lookup delay/failure, different target IPs/caching behavior.
Disk/JTL I/O Serialization/writes for result artifacts/logs. Large files, write latency, full disk, post-run artifact bottleneck.
Network capacity NIC/TCP bytes/packets/queues between injector and target. NIC saturation, drops/retransmission/queueing, achieved throughput ceiling.

4. Configured load is not achieved load

Configured load is Thread Groups, loops, timers/rates and target selection. Achieved load is the samples/concurrency/RPS the generator actually delivered after JVM/OS/network/result costs. Usable generator capacity is the highest controlled workload that meets your generator-headroom/validity criteria—not the largest number you can type into “Number of Threads.”

5. JMeter's current JVM launcher baseline

Current JMeter launcher defaults are 1 GiB heap and G1GC. JMeter's Getting Started guide explicitly says 1 GiB might not be enough; the correct heap depends on the plan and thread count. Do not read that as “always increase heap.” First determine whether live-set/GC pressure is the constraint.

Use a setenv.bat/setenv.sh or process environment when a measured change is justified rather than editing the distributed JMeter launcher scripts in place.

6. CPU cost depends on plan and target speed

A faster target can make the generator work harder because threads finish sooner and execute more samples per second. CPU cost also rises with regex/JSON/XML processing, uncached scripts, compression, TLS, large payload hashing, listeners and result serialization. A “20% CPU at 1,000 threads” rule from another test tells you little about this plan.

7. Connection reuse versus socket churn

JMeter defaults to HttpClient4. The HTTP Request Use KeepAlive option works with the HttpComponents implementation. Reuse usually means a virtual user can issue many requests over fewer TCP connections; disabling it forces connection churn, additional TCP handshakes and more local source ports/TIME_WAIT state.

The chapter checkpoint intentionally begins with keep-alive disabled so socket/ephemeral-port pressure is observable at a safe scale, then turns it back on as the reversible plan correction.

8. DNS is a separate performance dependency

By default JMeter uses JVM DNS caching. DNS Cache Manager can provide per-thread/per-iteration behavior independent of JVM/OS caches, but it works only with HttpClient4. Do not clear/disable global OS/JDK DNS caches during a capacity test unless that is explicitly part of the modeled production behavior.

The mandatory generator benchmark uses 127.0.0.1 to remove DNS variability. DNS is inspected separately.

9. JTL is generator work

Every saved field must be serialized/written. Chapter 22 established the lean default: CLI, CSV, no bodies/request/response headers unless justified. For generator sizing, record JTL bytes per sample and disk free space so result output cannot silently become the limiting subsystem.

Preserve the matching jmeter.log for every profile run. JTL records sample outcomes; jmeter.log preserves engine/runtime/script/connection warnings and errors that may explain generator pressure even when samples appear successful.

10. State checklist before tuning

State Read-only question
JMeter/JVM Exact JMeter/Java, heap/GC flags, heap occupancy, GC counts/time, process CPU/threads?
Thread/arrival Threads/loops/timers/rate and expected/achieved samples/RPS?
Components Listeners/assertions/scripts/parsers/payload sizes/keep-alive and connection settings?
Variables/properties/data Any runtime object creation, huge variables/data, per-thread caches?
Protocol/session HttpClient4/keep-alive/TLS/DNS/redirect/retry/session behavior?
OS Handles/FDs, sockets by state, dynamic-port range, process/file limits?
Network/DNS NIC bytes/drops, resolver/cache state, source address/routing?
Results/disk CSV/XML fields, response retention, JTL/log size, free disk/write pressure?
Target Service-wall time/errors/resource state independently stable?
Validity Does achieved work match configured work while injector maintains accepted headroom?

11. Read-only/non-destructive inspection first

Windows:

# 1) Find the JMeter JVM PID (inspect CommandLine before using it).
Get-CimInstance Win32_Process |
  Where-Object { $_.Name -eq "java.exe" -and $_.CommandLine -like "*ApacheJMeter.jar*" } |
  Select-Object ProcessId, CommandLine

# 2) JVM / process snapshots. Replace <PID>.
jcmd <PID> VM.version
jcmd <PID> VM.flags
jcmd <PID> GC.heap_info
jstat -gcutil <PID> 1000 12

Get-Process -Id <PID> |
  Select-Object Id,CPU,WorkingSet64,PrivateMemorySize64,Handles,Threads

# 3) TCP/socket state owned by the JVM.
Get-NetTCPConnection -OwningProcess <PID> -ErrorAction SilentlyContinue |
  Group-Object State | Select-Object Name,Count

# 4) Inspect the OS dynamic TCP port range; DO NOT change it in this lab.
netsh int ipv4 show dynamicport tcp
netsh int ipv6 show dynamicport tcp

# 5) DNS state / lookup path.
Resolve-DnsName localhost
Get-DnsClientCache | Select-Object -First 20

# 6) Network adapter counters.
Get-NetAdapterStatistics |
  Select-Object Name,ReceivedBytes,SentBytes,ReceivedDiscardedPackets,OutboundDiscardedPackets

# 7) Disk/result artifact.
Get-Item .\results\<run>\results.jtl |
  Select-Object FullName,Length,LastWriteTime
Get-Volume | Select-Object DriveLetter,SizeRemaining,Size

Linux:

# Identify the JMeter JVM.
jps -lv

# JVM heap/GC (replace PID).
jcmd PID VM.version
jcmd PID VM.flags
jcmd PID GC.heap_info
jstat -gcutil PID 1000 12

# Process CPU/memory.
ps -p PID -o pid,pcpu,pmem,rss,vsz,nlwp,etime,cmd
pidstat -p PID 1 10          # if sysstat/pidstat is installed

# File descriptors and sockets.
ls /proc/PID/fd | wc -l
ss -tanp
ulimit -n
cat /proc/sys/fs/file-max

# Dynamic local port range (read-only).
sysctl net.ipv4.ip_local_port_range

# DNS state.
getent hosts localhost
resolvectl statistics        # where systemd-resolved is present

# Network/disk state.
ip -s link
df -h .
du -h results/*/results.jtl

jcmd GC.heap_info is a medium-impact inspection command; use it at sensible checkpoints, not in a tight loop. JDK 17 documents jstat as experimental/unsupported, so retain the exact JDK build and corroborate with JMeter/OS evidence.

12. Optional generator-only reference

JMeter best practices notes that the built-in JavaTest sampler requires no network access and can help estimate raw JMeter execution throughput on a platform. A short JavaTest run can separate “JMeter/JVM overhead” from HTTP/network cost, but it is not a substitute for sizing the real HTTP plan.

13. DevOps connection

Generator capacity is infrastructure capacity. Treat each injector like a production service: inventory its CPU/RAM/NIC/storage/JVM/OS limits, monitor it, version its config, define headroom gates and scale horizontally only after one engine's usable capacity is measured. Chapter 25's distributed total-load math is only trustworthy when each engine can actually deliver its assigned share.

Knowledge check

Why can a faster server increase JMeter CPU usage?

Why is 1 GiB not a universal JMeter heap recommendation?

What does a large TIME_WAIT population suggest?

Why use 127.0.0.1 in the mandatory benchmark?

When is a target-capacity claim invalid even if JTL has no failures?

Next lesson

Profile one bounded generator end to end

Lesson 2 creates a synthetic local HTTP target, records JVM/CPU/socket/network/JTL evidence, runs a 1,000-sample connection-churn profile, restores keep-alive, and compares the exact same workload.

Official references and version notes

Version and compatibility note

Version-sensitive statements were rechecked against current primary documentation on 2026-09-05. The course baseline remains Apache JMeter 5.6.3 with a Java 17 JDK. JMeter 5.6.3 requires Java 8+; the 5.6.x changes page recommends Java 17 or later. Current JMeter launcher scripts default to a 1 GiB heap (-Xms1g -Xmx1g plus a 256 MiB metaspace cap) and G1GC with -XX:MaxGCPauseMillis=250/-XX:G1ReservePercent=20. Those defaults are a starting point, not a universal sizing rule. JMeter's HTTP sampler default is HttpClient4. Its retry count defaults to 0; its documented connection TTL defaults to 60 seconds. The HTTP Request Use KeepAlive option is effective with the Apache HttpComponents implementation and is the only plan change used in the checkpoint. JMeter's own best-practice guidance says effective thread capacity depends on hardware, plan design and how fast the target responds; CLI mode, minimal listeners, CSV and only required fields reduce generator cost. The mandatory lab never changes OS ephemeral-port ranges, file-descriptor limits, firewall/antivirus state or global DNS/JDK security settings.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.