Skip to content

[native] Fix a JNI reference leak and drop new[] from jstring_array_wrapper - #12568

Merged
simonrozsival merged 3 commits into
mainfrom
dev/simonrozsival/fix-jstring-array-wrapper
Sep 3, 2026
Merged

[native] Fix a JNI reference leak and drop new[] from jstring_array_wrapper#12568
simonrozsival merged 3 commits into
mainfrom
dev/simonrozsival/fix-jstring-array-wrapper

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Aug 28, 2026

Copy link
Copy Markdown
Member

PR relationship: No code prerequisite; this PR is based directly on main. The absolute CoreCLR totals below were measured in the former serialized stack.

Part of the drop-libc++ effort.

Split out of #12560, where it had been sitting alongside the DSO loader changes despite being unrelated to them.

jstring_array_wrapper had two problems.

A JNI local reference leak

jstring_wrapper::release () bailed out early when it had no UTF chars to release:

if (cstr == nullptr) {
return;
}

But jstring_array_wrapper::operator[] fetches the array element's reference eagerly, while cstr is only populated on the first get_cstr () call. So any element that was indexed but never read kept its local reference until the frame was popped. The reference and the UTF chars have independent lifetimes, so they are now released independently.

new[] / delete[]

The wrapper allocated its elements with new jstring_wrapper[len], which is where _Znam and _ZdaPv in host.cc.o came from. It now uses malloc() with explicit placement construction and destruction.

jstring_wrapper is not an implicit-lifetime type — it has a user-provided destructor — so malloc() alone cannot begin its lifetime; placement new is required. Its default constructor is private, with jstring_array_wrapper as a friend, which is what makes that legal here.

Results

beforeafter
host.cc.o refs119
CoreCLR total refs2321

All three runtime lanes build clean.

CopilotAI lite review requested due to automatic review settings August 28, 2026 12:36

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

Review tier: Lite
Findings: 1 High severity

New issues introduced by this change (1)
SeverityFinding
High severitysrc/​native/​common/​include/​runtime-base/​jni-wrappers.hh — 🤖 ❌ error Native memorylen * sizeof (jstring_wrapper) can overflow on 32-bit ABIs (since…
What changed in this PR

This PR updates the native JNI wrapper utilities to (1) correctly release JNI local references even when UTF chars were never requested, and (2) remove new[]/delete[] usage from jstring_array_wrapper to help reduce C++ runtime symbol dependencies as part of the “drop-libc++” effort.

Changes:

  • Fix jstring_wrapper::release() so it always releases the JNI reference, independently of whether UTF chars were fetched.
  • Replace new[]/delete[] allocation in jstring_array_wrapper with malloc() + placement-new construction + explicit destruction.
  • Add <new> include to support placement new.
FileDescription
src/​native/​common/​include/​runtime-base/​jni-wrappers.hhFixes JNI local ref lifetime handling and replaces new[] with malloc() + placement new to reduce C++ runtime dependencies.

Comment threadsrc/native/common/include/runtime-base/jni-wrappers.hh Outdated
@simonrozsivalsimonrozsival added the drop-libcpp Work to remove the libc++ dependency from Android NativeAOT label Aug 28, 2026
…rapper
`jstring_array_wrapper::operator[]` fetches the element's JNI reference on
first access, but the UTF characters behind it are only fetched later, when
something calls `get_cstr ()`. `jstring_wrapper::release ()` bailed out early
whenever `cstr` was null, so an element that was indexed but never read kept
its local reference until control returned to Java. Release the characters and
the reference independently instead.
The overflow storage used `new jstring_wrapper[]`/`delete[]`, which is where
`operator new[]` and `operator delete[]` entered `host.cc`. Allocate the array
with `malloc ()` and run the constructors and destructors explicitly. Placement
new is a compile-time construct, so it does not pull anything in from libc++.
This removes `_Znam` and `_ZdaPv` from `host.cc`, taking the CoreCLR libc++
reference count from 23 down to 21.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/fix-jstring-array-wrapper branch from 212b1fb to 98e08b1CompareSeptember 3, 2026 06:15
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/clr-drop-std-semaphore branch from e808135 to 54a7fa6CompareSeptember 3, 2026 06:15
@simonrozsival
simonrozsival changed the base branch from dev/simonrozsival/clr-drop-std-semaphore to mainSeptember 3, 2026 06:15
simonrozsivaland others added 2 commits September 3, 2026 10:20
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reject invalid array lengths and out-of-range access instead of returning a mutable sentinel that cannot release assigned JNI references. Document reference and storage ownership and simplify reassignment.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label Sep 3, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12568

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — 0 errors, 0 warnings, 1 suggestion. The reference and UTF-character lifetimes are now correctly independent, overflow is checked before heap allocation, explicit construction/destruction is balanced, and all 44 CI checks passed. I left one inline suggestion for direct regression coverage of the lazy-reference and heap-storage paths.

Generated by Android PR Reviewer for #12568 · gpt56 · 73.7 AIC · ⌖ 9.03 AIC · ⊞ 25.7K
Comment /review to run again

Comment threadsrc/native/common/include/runtime-base/jni-wrappers.hh
@simonrozsival
simonrozsival merged commit d252d01 into mainSep 3, 2026
44 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/fix-jstring-array-wrapper branch September 3, 2026 12:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drop-libcppWork to remove the libc++ dependency from Android NativeAOTready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers