GeezerVison is showing github.com — perf: guard ObjectTable with a monitor instead of StampedLock by edusperoni · Pull Request #2103 · NativeScript/android
Conversation
edusperoni
commented
Nov 11, 2026
•
edited by coderabbitai
Bot
Loading
Uh oh!
There was an error while loading. Please reload this page.
Description
ObjectTable (added in #2099) guarded its tables with java.util.concurrent.locks.StampedLock: write locks for mutations, optimistic reads elsewhere. On device, StampedLock runs interpreted. It lives in the boot classpath (core-oj) but outside the boot image, and an app's baseline profile can't compile boot-classpath methods. A profile of NativeScript core's apps/automated suite showed StampedLock.tryWriteLock/casState/writeLock/unlockWriteInternal running in the interpreter on the main thread.
This replaces it with synchronized (this). An uncontended monitor enter/exit is compiled into every caller. Reads now take the lock too, so the torn-read handling the optimistic path needed (findIdUnlocked, getUnlocked) is gone. A second commit makes the table concurrent on every API level. It was limited to API 24+ only because StampedLock needs it. On API 21–23, argument objects were resolved natively under the isolate lock and new ids took a separate path; now every level registers through findOrRegister. findIdOptimistic is merged into findId, and getOrCreateObjectId takes one lock instead of a lookup followed by a registration.
JNI microbenchmarks (debug.ns.jnibench, optimized release, SM-G780G; main and this branch interleaved, 2 passes × 3 runs; median ns):
| Benchmark |
main |
this PR |
|
new java.lang.Object() |
2763 |
1528 |
−45% (6/6 runs faster) |
lifecycle new java.lang.Object() [alloc, kept alive] |
2807 |
1520 |
−46% |
lifecycle JniBench.newObjectInJava() [alloc, kept alive] |
2015 |
1094 |
−46% |
static.newObjectOfManyClasses() [16 classes] |
1683 |
1097 |
−35% |
static.newObjectInJava() [new wrapper] |
1276 |
954 |
−25% |
static.newInstance(extended class) [Java-created] |
8974 |
7419 |
−17% |
new Object.extend({})() |
3975 |
3740 |
−6% |
| contention, 1 thread, no args |
3608 |
2153 |
−40% |
callback.noArgs() |
479 |
436 |
−9% |
| contention, 2 threads, 3 new objects |
6101 |
6305 |
+3% (1/6) |
| contention, 2 threads, 3 new objects of 16 classes |
6295 |
6487 |
+3% (0/6) |
| contention, 4 threads, 3 existing objects |
3343 |
3546 |
+6% (0/6) |
The other rows are within ±2%. Even after the JIT has compiled it, StampedLock's write path was costly.
Known trade-off: when 2–4 Java threads call into JS at the same time, their object-id lookups used to be lock-free optimistic reads and now contend on the monitor: +3..+6% in those benchmarks. Apps run JS on one thread in the common case, where this is a clear win.
Real apps: no measurable difference on the template app's cold start (497/491 vs 503/494 ms) or on a 1000-tile flex-wrap mount. The phone was thermally throttling during that comparison, so those numbers only rule out a large effect.
Does your commit message include the wording below to reference a specific issue in this repo?
No issue.
Related Pull Requests
Does your pull request have unit tests?
Yes. The ObjectTable JVM test for concurrent readers and a writer now requires every read of a stable entry to hit; it used to allow optimistic misses. The test for disabled optimistic reads is gone with the flag. JVM tests: 29/29.
Full device suite:
- API 36 emulator: 1551 specs, 0 failures.
- API 23 emulator (arm64): 1551 specs, 3 failures, the same 3 that
main fails on that emulator:
- a proxy test that matches
ByteBuffer.toString()'s newer cap= format;
- two HTTP module-import tests that time out.
On API 23 the harness also reports "App has crashed - ErrorReportActivity" for both main and this PR. run-as ... ls exits 0 there even when the file is missing, so the harness's checks misfire; the results were read from the XML directly.
Summary by CodeRabbit
- Bug Fixes
- Improved the reliability of object lookups when multiple threads access the runtime, helping ensure stable objects return the correct identifiers.
- Object registration, lookup, retrieval, and removal now use consistent synchronization, reducing the risk of inconsistent results during concurrent access.
- Updated object argument handling to register objects without an existing identifier and correctly mark newly registered objects.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
vallemar reacted with rocket emoji
StampedLock lives in the boot classpath but outside the boot image, so on
device it runs interpreted, and an app's baseline profile can't compile it.
A monitor's uncontended enter/exit is compiled into every caller. Reads now
take the lock too, which also removes the torn-read handling the optimistic
path needed.
coderabbitai
Bot
commented
Nov 11, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|

No actionable comments were generated in the recent review.
Walkthrough
-
ObjectTable removes StampedLock and optimistic reads. It synchronizes table reads, registration, and mutations on the table monitor. Runtime uses the synchronized lookup and registration methods, and concurrency tests require stable lookup results.
Changes: ObjectTable synchronization
| Layer / File(s) |
Summary |
Monitor-synchronized table access test-app/runtime/src/main/java/com/tns/ObjectTable.java |
ObjectTable removes StampedLock and optimistic lookup paths. It synchronizes lookup, registration, retrieval, containment, weakening, and removal on the table monitor. Batch weakening holds the monitor across the batch. |
Runtime lookup and registration test-app/runtime/src/main/java/com/tns/Runtime.java |
Runtime uses findOrRegister for object ID lookup and registration, and uses findId for native dispatch. Argument packaging marks newly registered objects with FRESH_ARG. |
Concurrent access tests test-app/runtime/src/test/java/com/tns/ObjectTableTest.java |
The concurrent-read test uses findId and requires the expected ID. Tests no longer configure optimistic reads or assert that optimistic reads return NO_ID. |
Estimated code review effort: 2 (Simple) | ~15 minutes
Change: Refactor
Possibly related PRs
- NativeScript/android#2099: Introduced ObjectTable and replaced the runtime's object maps; this PR changes ObjectTable's synchronization model.
Merge Risk: Minimal · up to fee28
-
The change replaces the StampedLock in ObjectTable with monitor synchronization to improve performance. No merge-blocking risk was found.
Pre-merge checks: 4
-
| Check name |
Status |
Explanation |
| Title check |
 |
The title clearly and concisely identifies the main change: replacing ObjectTable's StampedLock with a monitor. |
| Linked Issues check |
 |
Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check |
 |
Check skipped because no linked issues were found for this pull request. |
| Description Check |
 |
Check skipped - CodeRabbit’s high-level summary is enabled. |
Finishing Touches
-
Generate docstrings
-
- Commit to this branch
- Create a new PR
Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Share
-
Recent review info
-
Run configuration
-
Commits
-
Reviewing files that changed from the base of the PR and between 7f7ad3a and fee28fb.
Files selected for processing: 3
-
test-app/runtime/src/main/java/com/tns/ObjectTable.java
test-app/runtime/src/main/java/com/tns/Runtime.java
test-app/runtime/src/test/java/com/tns/ObjectTableTest.java
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
ObjectTable was only concurrent where StampedLock exists (API 24+). With a
monitor there is no reason to keep API 21-23 on the slower paths: argument
objects resolved natively under the isolate lock, and new ids registered
through a separate path. findIdOptimistic is now findId, and
getOrCreateObjectId and packageArgs register through findOrRegister alone.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|