Skip to content

Commit e634b3f

Browse files
committed
Fix major performance regression
LmdbJava Benchmarking infrastructure was updated in #262. This uncovered a major regression in releases 0.9.0 and 0.9.1. To identify the source of the regression, the updated LmdbJava Benchmarks were executed with 1,000,000 entries using 100 byte values and 4 byte integer keys. The sequential read benchmark was retrospectively executed against LmdbJava commits compiled using Java 25 as detailed in the following table. To rule out native library variability, LMDB 0.9.33 from the official Arch Linux x86_64 native package was used via the lmdbjava.native.lib system property. Benchmarks were run on workstation-grade hardware (64 GB RAM, AMD Ryzen 9 7900 12-Core Processor) to a 32 GB /tmp directory backed by tmpfs. The machine was not running any other workloads. Tests were executed for 1 minute per warmup, 1 minute per iteration, 3 warmups, 3 iterations, and 3 forks. As such each commit was tested for approximately 18 minutes to overcome jitter and noise found at shorter durations. It was not possible to test intermediate releases 0.6.3 or 0.7.0 due to JNR-FFI incompatibilities. As shown, the regression was introduced in commit 0c636f1 (#207). This followed the 0.8.3 release and was included in releases 0.9.0 and 0.9.1. The fix provided in this commit has been verified with the benchmark result shown below. In addition to the automated tests passing, a script was written that executed "mvn test -Dtest=GarbageCollectionTest" (which was provided in #207) for 1000 iterations with both Java 17 and 25. This comprehensively confirms this fix does not introduce the adverse behaviour initially identified in #207. The logical basis for the fix is also confirmed in the updated JavaDocs for ReferenceUtil. While there remains a 4.60% regression relative to the 0.0.1 baseline, this is lower than any release since 0.0.5. Further work to follow on automating benchmarking infrastructure and addressing other performance improvement opportunities. Commit Release Date Score (ms) Delta Notes ------------------------------------------------------------------------ 57f355f v0.0.1 2016-07-07 43.22 Baseline 6d001e8 v0.0.5 2017-02-08 43.97 +1.74% d3572ca v0.5.0 2017-07-05 45.29 +4.79% be2a15b v0.8.3 2023-02-04 45.26 +4.72% 3524995 2023-02-04 45.41 +5.07% Refactor Comparator handling 9cf97a5 2023-03-09 45.04 +4.21% mdb_reader_check API fix 0c636f1 2023-03-23 47.83 +10.67% Hold strong reference (REGRESSION) cbdaee6 2023-04-24 47.39 +9.65% Update formatting/GC test c5e02f7 2023-04-24 48.24 +11.61% Merge fixcorruptionbug f17b63f v0.9.0 2023-12-05 48.54 +12.31% 0025a48 v0.9.1 2025-02-20 47.61 +10.16% ddca4bd 2025-07-08 47.55 +10.02% Fix comparator lost buffer ref c91893a 2025-10-25 58.67 +35.74% Fix transient pointer (PEAK REGRESSION) 43ac84f 2025-10-26 55.08 +27.43% a15f4a7 2025-10-29 55.43 +28.24% Master before fix 19b0250 2025-10-30 45.21 +4.60% Empty method fix (REGRESSION ELIMINATED)
1 parent a15f4a7 commit e634b3f

File tree

1 file changed

+10
-9
lines changed

1 file changed

+10
-9
lines changed

src/main/java/org/lmdbjava/ReferenceUtil.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,18 @@ private ReferenceUtil() {}
3333
* but it was only introduced in Java 9. LmdbJava presently supports Java 8 and therefore this
3434
* method provides an alternative.
3535
*
36-
* <p>This method is always implemented as a synchronization on {@code ref}. <b>It is the caller's
37-
* responsibility to ensure that this synchronization will not cause deadlock.</b>
36+
* <p>This method works because HotSpot JIT-compilers prune dead locals based on method bytecode
37+
* analysis rather than optimized IR. As Vladimir Ivanov explains: "any usage of a local extends
38+
* its live range, even if that usage is eliminated in generated code". The method call at the
39+
* bytecode level is sufficient to keep the object alive through safepoints, preventing premature
40+
* garbage collection during native operations.
3841
*
39-
* @param ref the reference (null is acceptable but has no effect)
40-
* @see <a href="https://github.com/netty/netty/pull/8410">Netty PR 8410</a>
42+
* @param ref the reference
43+
* @see <a href="https://mail.openjdk.org/pipermail/core-libs-dev/2018-February/051312.html">
44+
* Vladimir Ivanov on reachabilityFence implementation</a>
4145
*/
4246
public static void reachabilityFence0(final Object ref) {
43-
if (ref != null) {
44-
synchronized (ref) {
45-
// Empty synchronized is ok: https://stackoverflow.com/a/31933260/1151521
46-
}
47-
}
47+
// Empty method body is intentional - the method call itself at bytecode level
48+
// extends the object's live range per HotSpot JIT behavior
4849
}
4950
}

0 commit comments

Comments
 (0)