View all services
Talk to QA Advisor
Browse the Knowledge Hub211 resources
/Tools Library/Apache JMeter
Tools Library · We run this

Apache JMeter review: what it is, tutorial and alternatives

Apache JMeter is a free, open source Java application for load and performance testing. Test plans are assembled in a desktop GUI from samplers, timers, extractors and listeners, saved as XML, and run from the command line, where each virtual user is a Java thread. It works at the protocol level across HTTP, JDBC, JMS, LDAP, FTP, TCP and mail.

QAble runs this tool on client engagements. The assessment below is from that experience.

last reviewed
13 October 2026
version covered
Apache JMeter 5.6.3
licence
Open source

Reviewed by Viral Patel

Co-Founder, QAbleLast reviewed 13 October 2026

At a glance

Apache JMeter at a glance

Official site
Vendor
Apache Software Foundation
Category
Performance testing
Licence
Open source (Apache 2.0)
Platforms
JVM
Languages
Groovy, Java, BeanShell
Integrates with
Jenkins, Taurus, InfluxDB, Grafana, BlazeMeter
First released
1998
Version reviewed
Apache JMeter 5.6.3

Verdict

Our verdict on Apache JMeter

Strengths

  • Protocol breadth in the core: HTTP, JDBC, JMS, LDAP, FTP, TCP, mail and shell commands with no extensions
  • GUI plus HTTP(S) Test Script Recorder lets testers build correlated, data-driven plans without writing code
  • One command produces an HTML dashboard with percentiles, APDEX, errors and throughput over time
  • Free under the Apache License 2.0, with built-in distributed mode and no paid tier held back
  • JSON, regular expression, boundary and CSS extractors cover most correlation without scripting
  • Large plugin ecosystem installed through the Plugins Manager, plus Groovy scripting for anything missing

Weaknesses

  • Each virtual user is a Java thread, so memory caps concurrency per load generator
  • JMX test plans are verbose XML that diff and merge poorly in code review
  • No pass or fail thresholds in a run; CI gating needs your own check on results
  • Not a browser: no JavaScript execution, so client-side timings need another tool
  • No release since 5.6.3 in January 2024; the next major will require Java 17

Best for

Teams that need load tests with no licence cost across more than HTTP: a database through JDBC, a message queue through JMS, LDAP, FTP or raw TCP in the same plan as the web traffic. It also suits test teams who prefer building scenarios visually over writing code, because the GUI, the recorder and the extractors let a tester produce a correlated, data-driven plan without programming. Its age is an asset here: almost every error message has been asked about and answered.

Not for

Teams who want load tests reviewed as code alongside the application, since a JMX file is GUI-generated XML and pull request diffs of it are hard to read. Also a weaker fit when a single machine must hold very high concurrency, because every virtual user is a Java thread, and for anyone who needs browser-side timings, because JMeter does not execute JavaScript. For JavaScript or Python teams, k6 or Locust will feel more natural.

Setup cost

Moderate. Install Java, unpack the archive and a first plan runs within the hour. The real work is building a plan that behaves like users: recording a journey, correlating every token and session value with extractors, feeding users from a CSV Data Set Config, adding think time, and moving thread counts and hosts into properties. Distributed runs add an RMI keystore, open firewall ports and identical installs on every load generator.

Maintenance burden

Moderate to high. Correlation breaks whenever the application changes how it issues tokens or IDs, and finding the broken extractor in a large plan is slow. JMX files merge badly, so we keep one owner per plan and split shared steps into Test Fragments. Plugin versions must match on every machine that runs a plan, and JMeter itself has not had a release since 5.6.3 in January 2024, so library upgrades arrive slowly.

Honest limitations

One thread per user, so concurrency per machine is bounded by memory and CPU, and the default heap is 1 GB. No JavaScript execution or rendering. No pass or fail thresholds built into a run: gating needs a script over the results or an external tool. Distributed mode multiplies the full plan on every server rather than sharing the load. The GUI must not generate real load, and the XML format resists review.

How it works

What is Apache JMeter and how does it work?

A test plan is a tree with scoping rules

Everything in JMeter is an element in a tree: thread groups, samplers that send requests, config elements such as HTTP Request Defaults and the Cookie Manager, timers, pre- and post-processors, assertions and listeners. Where an element sits decides what it applies to. A timer placed under a thread group runs before every sampler in that scope; placed under one sampler, it runs only before that one. The same rule governs extractors and assertions. Most confusing behaviour in a plan, such as a delay that applies twice or an extractor that overwrites a variable, comes from scope. The GUI saves the tree as a .jmx XML file, which is what the command line runs.

Threads are users, and the model is closed

A Thread Group starts a fixed number of threads, each one a virtual user that runs the plan in a loop. Ramp-up spreads thread start times over a period, with the first thread starting at once, and the group ends after a loop count or a duration. This is a closed model: a thread sends its next request only after the previous one returns, so when the server slows down, the request rate falls with it. JMeter 5.5 added an experimental Open Model Thread Group that schedules arrivals by rate instead, for example rate(10/sec) random_arrivals(5 min), which keeps pressure constant whatever the response time.

Protocol level, not a browser

JMeter sends requests and reads responses; it does not render pages or execute the JavaScript in them, as its own documentation states. An HTTP sampler can fetch embedded resources such as images and stylesheets, and the Cookie Manager and Cache Manager imitate a browser’s session and cache, but a single-page application’s client-side work is invisible. That is why JMeter scales further per machine than browser-based load and why correlation matters: any value the browser would have computed or read from a response, the plan must extract and send back itself.

Samples flow to a results file

Every sampler produces a sample result with timings, size, status and success. Listeners consume them: View Results Tree shows each request for debugging, while a run started with -l writes them to a JTL file, CSV by default. In command line mode the summariser prints running totals to the console, and the HTML dashboard is generated from the JTL either at the end of the run with -e -o or later with -g. Because results are plain CSV, the same file can be re-analysed, merged or fed into another tool.

Distributed mode copies the plan to every server

For more load than one machine can produce, a controller sends the plan over Java RMI to jmeter-server processes on other machines and collects their samples. Each server runs the whole plan, so 1,000 threads on six servers means 6,000 threads. Data files are not sent, so every server needs its own copies. Since JMeter 4.0, RMI uses SSL by default, which means a keystore must be generated and copied to every node before the first distributed run, or SSL explicitly disabled. All nodes should run exactly the same JMeter version.

Strengths

What it does well

Protocols beyond HTTP in the core distribution

JDBC, JMS, LDAP, FTP, TCP, SMTP, POP3, IMAP and OS process samplers ship with JMeter. A realistic test of an order flow can hit the web API, check the database row and put a message on a queue from one plan and one results file. k6 and Gatling concentrate on HTTP and its neighbours such as WebSockets, so this breadth without extensions is where JMeter still has few open source rivals.

Correlation without writing code

The JSON Extractor, Regular Expression Extractor, Boundary Extractor, CSS Selector Extractor and XPath2 Extractor each pull a value from a response into a variable, with a default value when nothing matches and a match number when several do. Placed as a child of the request that returns the token, they make dynamic sessions work without scripting, which is what lets non-programming testers maintain realistic plans.

Data-driven users from CSV files

CSV Data Set Config reads one line per iteration into named variables, shared across all threads by default so each user gets different credentials or search terms. Recycle on EOF and Stop thread on EOF decide what happens when the file runs out. Using real, varied data matters because caches and database plans behave very differently when every user requests the same record.

Pacing and arrival-rate control

Constant, Uniform Random and Gaussian timers add think time. The Constant Throughput Timer holds a target in samples per minute, and the Precise Throughput Timer models Poisson arrivals over a given period. With the Open Model Thread Group, these let a plan aim for a business volume such as orders per hour rather than a thread count, which is what most performance requirements are written in.

An HTML dashboard from one command

Adding -e -o folder to a command line run writes a self-contained HTML report: APDEX, a statistics table with three configurable percentiles (90, 95 and 99 by default), an error table, top five errors by sampler, and over-time graphs of response times, throughput, active threads and response codes. A statistics.json file is written beside it, which is what CI checks read.

Recording real journeys

The HTTP(S) Test Script Recorder acts as a proxy for a browser or mobile device and writes each request into the plan, grouped and named. For HTTPS it generates a temporary root certificate, ApacheJMeterTemporaryRootCA, which you install in the browser. A recording is the quickest way to capture every header and parameter a real journey sends, before you clean it up and correlate it.

Extensible with Groovy and plugins

JSR223 elements run Groovy, which is bundled and compiles and caches scripts, for logic no built-in element covers. The Backend Listener streams results live to InfluxDB or Graphite for Grafana dashboards. The community Plugins Manager from jmeter-plugins.org adds thread groups, graphs and samplers, and can install exactly the plugins a given plan needs.

Pricing

Apache JMeter pricing and licensing

JMeter is free and open source under the Apache License 2.0, maintained by the Apache Software Foundation, with no paid edition, no feature tiers and no limit on users or machines. The cost is infrastructure and time: load generators sized for your thread counts, network capacity between them and the system under test, and the hours spent building and maintaining correlated plans. Commercial cloud services run JMX plans on hosted load generators for teams that prefer to pay rather than operate machines.

  • Licence: Apache License 2.0, stated on the project site and in the repository. Commercial use, modification and redistribution are permitted.
  • Free tier: the entire tool. Distributed mode, the HTML dashboard, every sampler and the recorder are in the one download.
  • Plugins: the Plugins Manager and most jmeter-plugins.org plugins are free downloads from a community site, not the Apache project, so check each plugin’s licence and maintenance before relying on it.
  • How cost scales: with load generators. Because each user is a thread, high concurrency means more or bigger machines, plus the controller and network to collect their results.
  • Hosted options: services such as BlazeMeter and Azure Load Testing run JMeter plans on their own infrastructure under their own pricing; compare that against running cloud machines yourself.
  • Hidden cost: people. Correlation, data preparation and keeping plans working as the application changes take more time than the tool takes to install.

Checked against the vendor’s own pages on 13 October 2026. Pricing changes; confirm on the official site before buying.

Getting started

Apache JMeter tutorial: getting started

Setup, step by step

  1. Install a Java runtime: JMeter 5.6.3 needs Java 8 or later, and the project recommends Java 17.
  2. Download apache-jmeter-5.6.3 from jmeter.apache.org, verify the checksum, and unpack it.
  3. Start the GUI with bin/jmeter (bin/jmeter.bat on Windows) and build or record a small plan.
  4. Read thread count, duration and host from properties with __P so the same plan serves every environment.
  5. Close the GUI and run the plan from the command line with -n, writing results and the HTML report.
  6. Open report/index.html and check errors and percentiles before raising the load.

Install

# Java 8+ required (Java 17 recommended), then:
curl -O https://dlcdn.apache.org/jmeter/binaries/apache-jmeter-5.6.3.tgz
tar -xzf apache-jmeter-5.6.3.tgz
./apache-jmeter-5.6.3/bin/jmeter   # opens the GUI for building plans

# Run a plan for real in CLI mode, with an HTML report
./apache-jmeter-5.6.3/bin/jmeter -n -t smoke.jmx -l results.jtl -e -o report -Jhost=test.example.com -Jthreads=10

A first example

<?xml version="1.0" encoding="UTF-8"?>
<!-- smoke.jmx: normally saved by the GUI; shown here to make the structure visible -->
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Smoke" enabled="true">
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Users" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <intProp name="LoopController.loops">-1</intProp>
        </elementProp>
        <!-- __P reads a -J property and falls back to the default given -->
        <stringProp name="ThreadGroup.num_threads">${__P(threads,5)}</stringProp>
        <stringProp name="ThreadGroup.ramp_time">${__P(rampup,30)}</stringProp>
        <boolProp name="ThreadGroup.scheduler">true</boolProp>
        <stringProp name="ThreadGroup.duration">${__P(duration,120)}</stringProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET home" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">${__P(host,localhost)}</stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <stringProp name="HTTPSampler.path">/</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
        </HTTPSamplerProxy>
        <hashTree>
          <!-- Think time: 1000 ms plus up to 2000 ms random, before this request -->
          <UniformRandomTimer guiclass="UniformRandomTimerGui" testclass="UniformRandomTimer" testname="Think time" enabled="true">
            <stringProp name="ConstantTimer.delay">1000</stringProp>
            <stringProp name="RandomTimer.range">2000</stringProp>
          </UniformRandomTimer>
          <hashTree/>
        </hashTree>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

What trips people up first

Generating load from the GUI. The GUI is where plans are built and debugged, and pressing the green start button with 500 threads and View Results Tree enabled measures JMeter’s own struggle as much as the server. The project’s documentation is blunt: do not run load tests in GUI mode. Debug with a handful of threads, then close the GUI and run jmeter -n -t plan.jmx -l results.jtl, with View Results Tree disabled. The second hurdle is the 1 GB default heap; raise it through the HEAP environment variable before a large run rather than after an OutOfMemoryError.

In practice

Using Apache JMeter in practice

Organising plans for reuse and review

Keep one plan per scenario, such as browse, checkout or search, in a perf folder of the application repository, with CSV data files beside them and referenced by relative path. Put the host, protocol and port in HTTP Request Defaults, and read thread counts, ramp-up, duration and host from properties with __P, so the same plan runs against staging with -J overrides and nobody edits XML to change the load. Shared steps such as login belong in a Test Fragment called through a Module Controller or Include Controller. Wrap each user action in a Transaction Controller so the report shows business steps rather than individual requests. Because JMX diffs are hard to read, describe the intent of each change in the pull request, give each plan one owner, and keep the whole team and every load generator on the same JMeter version.

Correlating tokens and feeding users data

Record the journey, then find every value the server issues that a later request sends back: session IDs, CSRF tokens, order numbers. For each, add an extractor under the response that issues it and replace the hard-coded value with a variable, for example an Authorization header of Bearer ${authToken} in a Header Manager. A JSON Extractor handles most APIs. When the value needs logic, use a JSR223 PostProcessor in Groovy, not BeanShell: the project recommends JSR223 with Groovy for performance, because Groovy scripts are compiled and cached. Feed credentials from a CSV Data Set Config so each thread logs in as a different user, and give the file enough rows for the thread count.

// JSR223 PostProcessor, language groovy, placed under the POST /api/login request.
// Keep "Cache compiled script if available" ticked, and read JMeter variables
// with vars.get(): a ${...} reference inside a cached script is resolved only once.
import groovy.json.JsonSlurper

def body = new JsonSlurper().parseText(prev.getResponseDataAsString())
def token = body?.data?.accessToken

if (token) {
    vars.put('authToken', token as String)
} else {
    // Fail the sample so the problem shows up in the dashboard's error table
    prev.setSuccessful(false)
    prev.setResponseMessage('No accessToken for user ' + vars.get('username'))
    log.warn('Login failed for {}', vars.get('username'))
}

Running JMeter in GitHub Actions

There is no official JMeter action, and you do not need one: install Java with actions/setup-java, download a pinned JMeter release, verify its checksum and run in CLI mode. JMeter has no built-in pass or fail criteria, so a completed run is not a passing run. The dashboard step writes statistics.json, and a jq check on the Total entry fails the job when the error percentage or a percentile crosses your threshold. Upload the report and jmeter.log as artifacts whatever the result. Keep CI runs short and modest, a few minutes at a fraction of peak, to catch regressions on every change, and run full-scale tests from dedicated load generators where the CI runner cannot become the bottleneck.

# .github/workflows/load-smoke.yml
name: load-smoke
on:
  workflow_dispatch:
  schedule:
    - cron: '0 2 * * *'
jobs:
  jmeter:
    runs-on: ubuntu-latest
    env:
      JMETER_VERSION: '5.6.3'
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: '17'
      - name: Install JMeter
        run: |
          BASE=https://archive.apache.org/dist/jmeter/binaries
          curl -sSLO "$BASE/apache-jmeter-$JMETER_VERSION.tgz"
          curl -sSLO "$BASE/apache-jmeter-$JMETER_VERSION.tgz.sha512"
          sha512sum -c "apache-jmeter-$JMETER_VERSION.tgz.sha512"
          tar -xzf "apache-jmeter-$JMETER_VERSION.tgz"
          echo "$PWD/apache-jmeter-$JMETER_VERSION/bin" >> "$GITHUB_PATH"
      - name: Run the plan in CLI mode
        run: |
          jmeter -n -t perf/checkout.jmx -l results.jtl -j jmeter.log -e -o report \
            -Jhost=staging.example.com -Jthreads=20 -Jduration=300
      - name: Gate on errors and 95th percentile
        # pct2ResTime is the 95th percentile with default report settings
        run: jq -e '.Total.errorPct < 1 and .Total.pct2ResTime < 800' report/statistics.json
      - uses: actions/upload-artifact@v7
        if: always()
        with:
          name: jmeter-report
          path: |
            report
            jmeter.log

Spreading load across machines

When one generator runs out of CPU or memory, add servers rather than threads. Install the same JMeter and Java versions on every node, generate an RMI keystore once with the bundled script, and copy rmi_keystore.jks into the bin folder of every server and the controller. The generated key is valid for seven days, so automate its creation or a weekly run will fail. Fix the server’s RMI port with server.rmi.localport so firewall rules can be written, copy CSV files to each server, and remember that thread counts multiply by the number of servers. Start the run from a CLI controller and pass properties to the servers with -G, since -J only sets them locally.

# Once, from apache-jmeter-5.6.3/bin on any node.
# Answer "rmi" to the first question so it matches server.rmi.ssl.keystore.alias.
./create-rmi-keystore.sh
# Copy bin/rmi_keystore.jks to bin/ on every server and on the controller.

# On each load generator: start the server on a fixed RMI port
./jmeter-server -Jserver.rmi.localport=50000

# On the controller: CLI mode, two servers, properties sent to both with -G,
# results collected locally, servers stopped at the end with -X
./jmeter -n -t checkout.jmx -R 10.0.0.11,10.0.0.12 \
  -Gthreads=200 -Gduration=1800 -Ghost=staging.example.com \
  -l results.jtl -e -o report -X

Reading results and keeping plans healthy

Read percentiles, not averages: the dashboard’s statistics table shows the 90th, 95th and 99th by default, and the response time over time graphs show whether the system degraded during the run or was slow from the start. Set APDEX thresholds in user.properties to match your own targets rather than the 500 and 1,500 millisecond defaults. For long soak tests, stream results through the Backend Listener to InfluxDB and Grafana so you can watch the run live. Keep plugins under control with the Plugins Manager’s command line, whose install-for-jmx command installs exactly what a plan needs on each machine. Re-record or re-check correlation after every significant release of the application, because a stale token extractor usually shows as a wave of 401 or 403 errors rather than slower responses.

Pitfalls

Common pitfalls

Running the load test from the GUI

Why it happens: The GUI is where the plan is built, so pressing start feels natural. Rendering samples, especially in View Results Tree, consumes CPU and memory on the load generator and distorts the measured response times.

The fix: Use the GUI only to build and debug with a few threads. Run real tests with jmeter -n -t plan.jmx -l results.jtl and disable View Results Tree and similar listeners for load runs.

Replaying recorded tokens instead of correlating them

Why it happens: A recording captures the session ID, CSRF token and IDs that were valid at recording time. Replayed later, every request either fails or quietly exercises an error page, which can make the system look fast.

The fix: Add an extractor under each response that issues a dynamic value, use the variable in later requests, and add a Response Assertion on content that only appears on success, so silent failures are counted as errors.

Threads with no think time

Why it happens: Without timers each thread sends its next request the instant a response arrives, so the request rate depends on server speed and a thread count says little about how many real users the test represents.

The fix: Add a Uniform Random or Gaussian timer at the right scope for think time, and use the Constant or Precise Throughput Timer, or the Open Model Thread Group, when the requirement is a rate such as orders per hour.

Surprises in distributed mode

Why it happens: Each server runs the whole plan, so thread counts multiply; CSV files are not sent by the controller; and the RMI keystore made by create-rmi-keystore expires after seven days, so a setup that worked last week fails to connect.

The fix: Divide intended threads by server count, distribute data files with the plan, regenerate the keystore as part of provisioning, and pass properties to servers with -G rather than -J.

Slow or broken scripting elements

Why it happens: Old tutorials use BeanShell, which the project now discourages for performance, or JavaScript in JSR223 elements, but the Nashorn JavaScript engine was removed from Java 15. Scripts that embed variable references also break once compiled and cached.

The fix: Use JSR223 elements with Groovy and Cache compiled script ticked, read variables with vars.get rather than embedded references, and prefer a built-in extractor whenever one can do the job.

Following tutorials for older versions

Why it happens: Articles written before JMeter 5.0 refer to Test Action, renamed Flow Control Action in 5.0, and CSS/JQuery Extractor, renamed CSS Selector Extractor. Guides older than 4.0 also omit the RMI SSL keystore, so their distributed setup fails.

The fix: Check element names against the current component reference and read the changes history for every version between the tutorial and 5.6.3. Prefer the official user manual over third-party walkthroughs.

Compared

Apache JMeter alternatives compared

ToolTest formatLicenceNon-HTTP protocolsDistributed loadBuilt-in reporting
Apache JMeterXML test plan built in a GUIApache 2.0, entirely freeJDBC, JMS, LDAP, FTP, TCP, mail built inBuilt in over RMI, SSL keystore neededHTML dashboard from the command line
k6JavaScript or TypeScript codeAGPL-3.0; hosted Grafana Cloud k6 optionWebSockets, gRPC; more via xk6 extensionsk6 Operator on Kubernetes, or Grafana CloudEnd summary, web dashboard with HTML export
GatlingJava, Kotlin, Scala, JavaScript or TypeScript codeApache 2.0; paid Gatling EnterpriseWebSocket, SSE, JMS in open sourceGatling Enterprise onlyStatic HTML report per run
LocustPython codeMIT, freeAny protocol with a Python clientBuilt in: master and worker processesLive web UI, HTML and CSV export
LoadRunnerVuGen scripts, C by defaultCommercial; free trial offeredOver 180 protocols and technologiesController drives remote load generatorsSeparate Analysis tool

Head-to-head comparisons

k6 vs JMeter

FAQ

Frequently asked questions

What is Apache JMeter?

Apache JMeter is a free, open source Java application for load and performance testing. Test plans are assembled in a desktop GUI from samplers, timers, extractors and listeners, saved as XML, and run from the command line, where each virtual user is a Java thread. It works at the protocol level across HTTP, JDBC, JMS, LDAP, FTP, TCP and mail.

What are the best Apache JMeter alternatives?

The alternatives most often weighed against Apache JMeter are k6, Gatling, Locust and LoadRunner. The comparison on this page sets them side by side on test format, licence, non-http protocols, distributed load and built-in reporting, and the verdict above says where Apache JMeter is the wrong choice and which of them to pick instead.

Is JMeter free?

Yes. Apache JMeter is open source under the Apache License 2.0, with no paid edition and no limits on users, machines or features. Distributed mode, the recorder and the HTML dashboard are all in the free download. What you pay for is infrastructure: load generator machines, or a commercial service such as BlazeMeter or Azure Load Testing if you want hosted generators to run your JMX plans.

Is there a JMeter 6?

Not as a release. On 13 October 2026 the download page offers 5.6.3, released in January 2024, as the current version. The development branch in the apache/jmeter repository is versioned 6.0.0, and its draft change notes say it will require Java 17 or later, remove the deprecated MongoDB elements and stop sending expired cookies. Until it ships, 5.6.3 is the version to use, and running it on Java 17 now makes a later upgrade simpler.

Which Java version does JMeter need?

JMeter 5.6.x needs Java 8 or later to run, and the project recommends Java 17 or later. Use the latest update of whichever major you choose, as the getting started guide advises for security and performance, and use the same Java version on every machine in a distributed test, as the remote testing guide asks. The next major, still unreleased, will require Java 17, so choosing 17 now avoids a second migration.

JMeter vs k6: which should I choose?

Choose JMeter when you need protocols such as JDBC, JMS or LDAP alongside HTTP, when testers prefer a GUI to code, or when an existing JMX estate already works. Choose k6 when developers own performance tests and want them as JavaScript or TypeScript in the repository, reviewed like other code and gated by thresholds in CI. k6 covers HTTP, WebSockets and gRPC natively and needs extensions for databases or queues, where JMeter has samplers built in.

How many users can one JMeter machine simulate?

There is no fixed number, and the project’s best practices page deliberately gives none. It depends on the machine, the plan, think time, response sizes and how fast the server answers. Find out empirically: run on the actual generator with CLI mode, minimal listeners and a sized heap, raise threads in steps, and watch the generator’s CPU, memory and garbage collection. When the generator saturates before the server, add servers rather than threads.

Can JMeter test front-end performance or single-page applications?

Only at the network level. JMeter does not execute JavaScript or render pages, so it measures the API calls and resources a page requests, not the time a user waits for content to appear. Record the browser’s traffic to load test the back end of a single-page application, and measure client-side timings separately with a browser-based tool.

How do I record a JMeter script?

Add the HTTP(S) Test Script Recorder from the Recording template or the Non-Test Elements menu, start it, and point the browser’s proxy at its port, 8888 by default. For HTTPS, install the generated ApacheJMeterTemporaryRootCA certificate in the browser. Exclude static resources you do not want, record the journey, then correlate dynamic values and add think time before running it under load.

Is JMeter still worth learning in 2026?

Yes, for performance testers. Existing JMX estates need maintaining, hosted services such as BlazeMeter and Azure Load Testing run JMX plans directly, and its concepts of correlation, pacing, closed and open workload models and percentile reporting carry over to every other tool. Learn a code-first tool such as k6, Gatling or Locust as well, because new teams increasingly want load tests written as code.

Alternatives

Apache JMeter alternatives worth weighing

Resources

Where to go next

Keep going

Prepare, or get it done

Ready

Picking the wrong tool costs more than picking late.

QAble advises on toolchain decisions and then implements them, with engineers who have run these tools on real delivery. Start with a free QA audit.

Talk to QA Advisor