IBAN Commons Benchmarks

May 11, 2026 ยท View on GitHub

A performance comparison module using the Java Microbenchmark Harness (JMH) to evaluate throughput and memory allocation of different IBAN libraries in the Java ecosystem.

If you feel other libraries should be included, or have suggestions for this suite, please get in touch.

๐ŸŽฏ Benchmark Scope

Two benchmark groups cover the full validation spectrum:

BenchmarkLibraryDescription
bmv1iban-commons (de.speedbanking)High-performance ASCII-math validation โ€“ valid IBANs
bmv2iban4j (org.iban4j)Exception-based validation โ€“ valid IBANs
bmv3Apache Commons ValidatorRegex-based IBAN validation โ€“ valid IBANs
bmv4garvelink iban (nl.garvelink.oss)Object-oriented parsing โ€“ valid IBANs
bmv5jbanking (fr.marcwrobel)Feature-rich banking toolkit โ€“ valid IBANs
bmi1iban-commonsSame as bmv1 โ€“ invalid IBANs (rejection cost)
bmi2iban4jSame as bmv2 โ€“ invalid IBANs (rejection cost)
bmi3Apache Commons ValidatorSame as bmv3 โ€“ invalid IBANs (rejection cost)
bmi4garvelink ibanSame as bmv4 โ€“ invalid IBANs (rejection cost)
bmi5jbankingSame as bmv5 โ€“ invalid IBANs (rejection cost)

The test dataset uses compact (unformatted) IBAN strings across all supported countries, generated randomly per run to prevent JIT over-specialization. Each invalid IBAN is derived from a valid one via RandomIban.invalidString(), which applies one of six sabotage strategies with equal probability: incrementing a check digit (triggering a Mod-97 failure), replacing the country code with the non-registered code XY, substituting a valid but mismatched ISO 3166 country code, injecting a letter into the numeric BBAN section, swapping two adjacent characters (transposition), or truncating the string below the minimum structural length.

A note on -XX:-StackTraceInThrowable

All forks run with this JVM flag, which suppresses stack trace generation. This isolates the pure algorithmic cost of validation and makes the comparison fair for libraries that use exceptions for control flow (notably iban4j). It does not reflect default production behaviour. For a production-realistic measurement, remove the flag from @Fork and re-run.

โš™๏ธ Requirements & Environment

๐Ÿš€ Building and Execution

The project uses the maven-shade-plugin to produce a single executable JAR containing all dependencies including the JMH runner.

1. Build the Executable JAR

mvn clean package
# or simply (defaultGoal is clean package):
mvn

This produces target/iban-commons-benchmarks.jar and copies the run scripts to target/.

2. Run Automated Benchmarks

The provided scripts detect system information, configure SerialGC for minimal measurement noise, and โ€“ on Linux โ€“ pin execution to a single CPU core to minimise measurement jitter.

Linux:

./target/run-benchmarks.sh

Windows:

target\run-benchmarks.cmd

Or run the JAR directly with standard JMH options:

# Run all benchmarks
java -jar target/iban-commons-benchmarks.jar IbanBenchmarks

# Run only the valid-IBAN group with custom iteration settings
java -jar target/iban-commons-benchmarks.jar "bm[1-5]" -i 10 -r 5s

# Run with GC profiling
java -jar target/iban-commons-benchmarks.jar IbanBenchmarks -prof gc

Results are written as .log and .json to target/ and automatically archived to benchmarks/history/ for regression tracking.

๐Ÿ“Š Results & Visualization

To visualize results interactively:

  1. Go to JMH Visualizer.
  2. Drag and drop the .json file from target/ or benchmarks/history/.

๐Ÿ“Š Latest Performance Snapshot (2026-05-11)

Measured on Intel(R) Core(TM) i7-1165G7 @ 2.80GHz, OpenJDK 21.0.7, Linux, single core (taskset -c 0), SerialGC, -XX:-StackTraceInThrowable. 3 forks ร— 5 iterations ร— 2 s each.

Valid IBANs (best-case / accept path)

#LibraryThroughput (ops/s)Memory (B/op)vs. iban-commons
bmv1๐ŸŒŸ iban-commons4,648,012~0baseline
bmv5jbanking3,531,732298~1.3ร— slower
bmv3Apache Commons2,515,737442~1.8ร— slower
bmv2iban4j2,344,2291,133~2.0ร— slower
bmv4Garvelink1,684,427869~2.8ร— slower

Invalid IBANs (rejection path)

#LibraryThroughput (ops/s)Memory (B/op)vs. iban-commons
bmi1๐ŸŒŸ iban-commons8,139,924~0baseline
bmi5jbanking6,151,961172~1.3ร— slower
bmi3Apache Commons5,027,890247~1.6ร— slower
bmi4Garvelink1,825,503677~4.5ร— slower
bmi2iban4j1,644,0641,308~5.0ร— slower

Memory figures (B/op) from JMH gc.alloc.rate.norm profiler.

Key Takeaways

iban-commons is consistently fastest across both valid and invalid input. Its rejection path is actually faster than its accept path (~8.1 M ops/s vs. ~4.6 M ops/s), because many invalid IBANs are rejected early by length or country-code checks before the full Mod-97 computation is reached.

Memory allocation is effectively zero โ€” the char[]-based validation pipeline introduced in 1.8.5 eliminates all transient heap allocation (< 0.001 B/op measured, gc.count = 0 across all 15 measurement iterations). All competing libraries allocate between 172 B/op (jbanking, rejection path) and 1,308 B/op (iban4j, invalid path).

jbanking is the strongest challenger, ranking second on both paths (~3.5 M ops/s valid, ~6.2 M ops/s invalid) at a moderate memory cost (~298 B/op valid, ~172 B/op invalid).

Apache Commons ranks third on both paths (~2.5 M ops/s valid, ~5.0 M ops/s invalid). Its regex can short-circuit on structural failures, keeping its rejection performance competitive.

iban4j and Garvelink both incur significant allocation on the rejection path because their exception-based API constructs full exception objects even when -XX:-StackTraceInThrowable eliminates the stack trace overhead.

Note: Results from the previous snapshot (2026-04-19, ParallelGC, 2 forks ร— 4โ€“5 iter ร— 2 s) are not directly comparable to the current figures due to the GC configuration change (SerialGC) and the increased fork count (3 ร— 5). SerialGC is more conservative for allocation-heavy workloads, which accounts for the lower absolute throughput of competing libraries.