A performance comparison module using the Java Microbenchmark Harness (JMH) to evaluate throughput and memory allocation of different IBAN and BIC libraries in the Java ecosystem.
If you feel other libraries should be included, or have suggestions for this suite, please get in touch.
IbanBenchmarks has three groups, covering validation of valid and invalid IBANs as well as IBAN creation from components:
| Benchmark | Library | Description |
|---|---|---|
bmv1 |
iban-commons (de.speedbanking) |
High-performance ASCII-math validation – valid IBANs |
bmv2 |
iban4j (org.iban4j) |
Exception-based validation – valid IBANs |
bmv3 |
Apache Commons Validator |
Regex-based IBAN validation – valid IBANs |
bmv4 |
garvelink iban (nl.garvelink.oss) |
Object-oriented parsing – valid IBANs |
bmv5 |
jbanking (fr.marcwrobel) |
Feature-rich banking toolkit – valid IBANs |
bmi1 |
iban-commons |
Same as bmv1 – invalid IBANs (rejection cost) |
bmi2 |
iban4j |
Same as bmv2 – invalid IBANs (rejection cost) |
bmi3 |
Apache Commons Validator |
Same as bmv3 – invalid IBANs (rejection cost) |
bmi4 |
garvelink iban |
Same as bmv4 – invalid IBANs (rejection cost) |
bmi5 |
jbanking |
Same as bmv5 – invalid IBANs (rejection cost) |
bmb1 |
iban-commons |
IBAN creation from bank code and account number (DE) |
bmb2 |
iban4j |
Same as bmb1 |
bmb3 |
iban-commons |
IBAN creation with calculated national check digit (FR) |
The builder group only covers iban-commons and iban4j, the other libraries offer no comparable API to build an IBAN from its components. bmb3 only covers iban-commons, since iban4j expects the national check digit to be passed in.
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.
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.
BicBenchmarks compares BIC validation of iban-commons (bmv1/bmi1), iban4j (bmv2/bmi2) and jbanking (bmv3/bmi3), again on valid and invalid input.
- JDK – Java 21 or higher
- Build Tool – Apache Maven 3.9+
- OS – Linux (recommended for CPU affinity via
taskset) or Windows
The project uses the maven-shade-plugin to produce a single executable JAR containing all dependencies including the JMH runner.
mvn clean package
# or simply (defaultGoal is clean package):
mvnThis produces target/iban-commons-benchmarks.jar and copies the run scripts to target/.
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-iban-benchmarks.sh
./target/run-bic-benchmarks.shWindows:
target\run-iban-benchmarks.cmd
target\run-bic-benchmarks.cmdOr run the JAR directly with standard JMH options:
# Run all IBAN 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 "IbanBenchmarks.*bmv" -i 10 -r 5s
# Run with GC profiling
java -jar target/iban-commons-benchmarks.jar IbanBenchmarks -prof gcResults are written as .log and .json to target/ and automatically archived to benchmarks/history/ for regression tracking.
To visualize results interactively:
- Go to JMH Visualizer.
- Drag and drop the
.jsonfile fromtarget/orbenchmarks/history/.
Measured with iban-commons 1.8.13 on Intel(R) Core(TM) i7-1165G7 @ 2.80GHz, OpenJDK 25.0.3, Linux,
single core (taskset -c 0), SerialGC, -XX:-StackTraceInThrowable.
3 forks × 5 iterations × 2 s each. Competing libraries in the versions declared in pom.xml.
| # | Library | Throughput (ops/s) | Memory (B/op) | vs. iban-commons |
|---|---|---|---|---|
| bmv1 | 🌟 iban-commons | 5,755,902 | 0 | baseline |
| bmv5 | jbanking | 4,258,449 | 325 | ~1.4× slower |
| bmv2 | iban4j | 4,207,195 | 506 | ~1.4× slower |
| bmv3 | Apache Commons | 3,052,124 | 398 | ~1.9× slower |
| bmv4 | Garvelink | 1,906,954 | 725 | ~3.0× slower |
| # | Library | Throughput (ops/s) | Memory (B/op) | vs. iban-commons |
|---|---|---|---|---|
| bmi1 | 🌟 iban-commons | 10,883,875 | 0 | baseline |
| bmi5 | jbanking | 7,322,453 | 171 | ~1.5× slower |
| bmi3 | Apache Commons | 6,065,384 | 210 | ~1.8× slower |
| bmi2 | iban4j | 2,089,403 | 972 | ~5.2× slower |
| bmi4 | Garvelink | 1,980,023 | 559 | ~5.5× slower |
| # | Library | Throughput (ops/s) | Memory (B/op) |
|---|---|---|---|
| bmb1 | 🌟 iban-commons (DE) | 4,656,409 | 120 |
| bmb2 | iban4j (DE) | 2,401,856 | 1,524 |
| bmb3 | 🌟 iban-commons (FR, NCD) | 3,664,765 | 152 |
| # | Library | Valid (ops/s) | Invalid (ops/s) | Memory (B/op) |
|---|---|---|---|---|
| bmv1/bmi1 | 🌟 iban-commons | 34,404,044 | 49,769,526 | 0 |
| bmv3/bmi3 | jbanking | 32,826,481 | 30,565,190 | 46–48 |
| bmv2/bmi2 | iban4j | 17,927,819 | 8,616,491 | 144–215 |
Memory figures (B/op) from JMH gc.alloc.rate.norm profiler.
iban-commons is consistently fastest across valid and invalid IBANs and BICs, and it is the only library that validates without any heap allocation. Its IBAN rejection path is nearly twice as fast as its accept path, because many invalid IBANs fail the length or country code check before the Mod-97 computation is reached.
Compared to 1.8.12, measured in the same session, IBAN validation got about 6% faster on valid
and 9% faster on invalid input, since a single CountryValidator class now serves all countries.
jbanking remains the strongest challenger on IBANs and comes close on valid BICs.
iban4j has caught up on valid IBANs and now ranks with jbanking, but still allocates heavily
and falls back on the rejection path, where its exception-based API creates exception objects even
though -XX:-StackTraceInThrowable removes the stack trace cost. The same applies to Garvelink.
Note: Earlier snapshots were taken on OpenJDK 21 and partly with ParallelGC and fewer forks. They are kept in
benchmarks/history/but are not directly comparable to the figures above.