Skip to content

Release discarded library class loaders retained by cached options - #1743

Open
curforever wants to merge 1 commit into
java-native-access:masterfrom
curforever:fix/library-options-classloader-lifetime
Open

curforever wants to merge 1 commit into
java-native-access:masterfrom
curforever:fix/library-options-classloader-lifetime

Conversation

@curforever

Copy link
Copy Markdown

Closes #1742.

Native.typeOptions has weak Class keys but strong option-map values. Its enclosing-library value references the library Class; a child-loaded TypeMapper or an explicit CLASSLOADER option can also retain the loader. Consequently discarded library class loaders remain reachable through the static cache.

Associate cached options with their Class using ClassValue<AtomicReference<Map<String,Object>>> on JVMs. The reference preserves get/put visibility and the existing shared options-map identity, including nested classes. Public APIs, JNI and other caches remain unchanged.

Older Android runtimes lack ClassValue. They retain the existing weak-map implementation in a separate backend; this change does not fix the Android cache leak. Include a narrowly scoped -dontwarn java.lang.ClassValue consumer rule in JARs and the AAR, since the unused JVM backend otherwise causes R8 to reject an old Android boot class path. Three build.xml resource-packaging lines implement that compatibility requirement.

Validation on Windows:

  • Four regressions exercise an actual C abs call through an isolated loader: default options, child-loaded mapper, explicit loader option, and live-class options retention. Same final tests: 3 failures before / 4 passing after; all four also pass on Java8 and Java21. GC collection checks use bounded waits.
  • ant -D-native=true dist succeeds, including platform compilation and Javadoc. Java release8 remains unchanged; Java-only changes use the upstream JNI binary, as allowed by CONTRIBUTING. Unchanged testlib sources were compiled locally for native tests.
  • Both changed Java files pass the existing Checkstyle rules. Global Checkstyle retains an unrelated X11 header error reproduced on original sources.
  • Core tests are not fully green: Java21 reports 642 original / 646 fixed XML records with the same 3 ASCII-mapping failures and 1 DirectTypeMapperTest fork-crash placeholder; Java8 has that same fork crash only in both versions. No new failure was observed. The crashed fork means these counts do not establish complete-suite execution.
  • 15 scoped platform tests pass. The unfiltered platform suite was not run because it contains system-clock and recycle-bin mutations.
  • A JVM loader probe denying ClassValue loads the Android backend without resolving the JVM backend. R8 9.5.23 with official SDK API25/minApi21 fails on a control JAR with only the consumer rule removed; final JAR and AAR classes pass with automatically consumed embedded rules. No Android device/emulator execution is claimed.

Implementation and validation were assisted by OpenAI Codex. No human review or full platform/native/Android matrix pass is claimed.

@matthiasblaesing

Copy link
Copy Markdown
Member

No human review or full platform/native/Android matrix pass is claimed.

Does this mean the code was generated from Codex and not test/reviewed by you?

Comment thread CHANGES.md
Bug Fixes
---------

* Allow discarded library class loaders to be collected on JVMs by associating cached options with their classes. Android retains the existing cache for compatibility with runtimes without `ClassValue`. See [#1742](https://github.com/java-native-access/jna/issues/1742).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please follow the established format you can see in the entry directly below this entry.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native.typeOptions never releases library interfaces, leaking classloaders

2 participants