Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leaks in methods looking up Class objects #109

Closed
dmitry-timofeev opened this issue Sep 8, 2018 · 1 comment · Fixed by #398
Closed

Memory leaks in methods looking up Class objects #109

dmitry-timofeev opened this issue Sep 8, 2018 · 1 comment · Fixed by #398
Assignees
Labels
breaking A patch that introduces breaking changes (or an issue requiring such patch) bug 🐛 help wanted ❤️
Milestone

Comments

@dmitry-timofeev
Copy link
Contributor

dmitry-timofeev commented Sep 8, 2018

Overview

JNIEnv methods accepting Desc<JClass> leak a local reference to the Class object if they create a new local reference.

Description

If an implementation of Desc#lookup that creates a new local reference is passed to the JNIEnv methods, the created local reference will be leaked (resulting in a leak of native JVM heap and a (potential) leak of referenced Java object). E.g.,

env.call_static_method("java/lang/Math", "abs", "(I)I", &[JValue::from(-1 as jint)])

will leak a single local reference to an instance of Class<java.lang.Math> created here each time it is called.

There are about two dozens of Desc#lookups in the codebase.

Which client code is NOT affected (benign leaks)

Such leaks can be considered benign if all created local references are freed later:

  • Refs created in native methods called from Java — freed by the JVM when the native method returns.
  • Refs created in a native method attached briefly to the JVM — freed after the thread is detached.
  • Refs created in a lambda passed to with_local_frame — freed after the last statement in that lambda.

Which client code IS affected

Code using affected methods in:

  • Native threads attached to the JVM for a long or indefinite time. Although such code shall generally use with_local_frame/auto_local to manage the references created by the user, it must be noted that it's not required if it performs only simple calls that do not expose any references that have to be deleted. In other words, it is reasonable to expect that the library methods do not leak — and that's the specified behaviour of the C APIs:

To ensure that programmers can manually free local references, JNI functions are not allowed to create extra local references, except for references they return as the result.

  • Long-running loops.

How to reproduce

See #108

Possible solutions

  1. Always perform lookups in a local frame to ensure we delete refs if we create them. Slight performance regression — need to evaluate.
  2. Have methods with distinct signatures that perform lookups and delete manually in them. A certain UX regression.
  3. Distinguish (?) somehow when we do create a reference during lookup:
    • By using a combination of new_local_ref + AutoLocal. If a lookup is performed — the result is simply wrapped in an auto_local. If an existing reference is returned (e.g., JClass -> JClass), it is first duplicated with new_local_ref to keep the original intact, and then wrapped in auto_local. Slight performance regression in case the existing reference is used — unneeded new_local_ref + delete_local_ref
    • By returning a structure similar to auto_local that remembers if it shall delete the local reference (or retrofitting such functionality in AutoLocal). No performance hit when it is not needed, but higher complexity
  4. (?)

See Also

@dmitry-timofeev dmitry-timofeev self-assigned this Sep 8, 2018
dmitry-timofeev added a commit to dmitry-timofeev/jni-rs that referenced this issue Sep 8, 2018
Benchmark `call_static_method` with extra Push/PopLocalFrame
ensuring there are no leaks.

See jni-rs#109
@koutheir
Copy link
Contributor

koutheir commented Nov 1, 2018

Which client code is NOT affected (benign leaks)

Such leaks can be considered benign if all created local references are freed later:

* Refs created in native methods called from Java — freed by the JVM when the native method returns.

If the native method implementation deals with large arrays of objects, then it might run out of local references storage sooner.

To ensure that programmers can manually free local references, JNI functions are not allowed to create extra local references, except for references they return as the result.

From a library user perspective, it is reasonable to assume this.

1. Always perform lookups in a local frame to ensure we delete refs _if_ we create them. _Likely to be a performance regression — need to evaluate._

Also likely to consume more memory due to the additional local references stacks created.

@dmitry-timofeev dmitry-timofeev pinned this issue Feb 9, 2019
@dmitry-timofeev dmitry-timofeev added the breaking A patch that introduces breaking changes (or an issue requiring such patch) label Jun 21, 2019
@rib rib added this to the 0.21 milestone Nov 20, 2022
argv-minus-one added a commit to argv-minus-one/jni-rs that referenced this issue Dec 5, 2022
…now `!Copy`.

Note that it is now sometimes necessary to use turbofish syntax with `Desc::lookup`, since the return type of `Desc::<T>::lookup` is not necessarily `T` any more (it might be `&T` or something instead). For example, if you need a `JThrowable` and the compiler can't figure that out, write `Desc::<JThrowable>::lookup(input, env)` instead of `input.lookup(env)`. User code doesn't usually use `Desc` directly, so this shouldn't be too much of a problem.

This new design has the side benefit that looked-up values can be wrapped in `AutoLocal`, which fixes jni-rs#109.
argv-minus-one added a commit to argv-minus-one/jni-rs that referenced this issue Dec 29, 2022
…#392). Fix local reference leaks (jni-rs#109). Extensive breaking API changes (see changelog).

Closes jni-rs#392. Closes jni-rs#381. Closes jni-rs#109.

See jni-rs#392 for the discussion that led to this commit.
argv-minus-one added a commit to argv-minus-one/jni-rs that referenced this issue Dec 29, 2022
…#392). Fix local reference leaks (jni-rs#109). Extensive breaking API changes (see changelog).

Closes jni-rs#392. Closes jni-rs#381. Closes jni-rs#109.

See jni-rs#392 for the discussion that led to this commit.
argv-minus-one added a commit to argv-minus-one/jni-rs that referenced this issue Jan 3, 2023
…#392). Fix local reference leaks (jni-rs#109). Extensive breaking API changes (see changelog).

See jni-rs#392 for the discussion that led to this commit.

Closes jni-rs#392
Closes jni-rs#381
Closes jni-rs#109

Co-authored-by: Robert Bragg <robert@sixbynine.org>
argv-minus-one added a commit to argv-minus-one/jni-rs that referenced this issue Jan 5, 2023
…#392). Fix local reference leaks (jni-rs#109). Extensive breaking API changes (see changelog).

See jni-rs#392 for the discussion that led to this commit.

Closes jni-rs#392
Closes jni-rs#381
Closes jni-rs#109

Co-authored-by: Robert Bragg <robert@sixbynine.org>
@rib rib closed this as completed in 5539c55 Jan 17, 2023
@rib rib closed this as completed in #398 Jan 17, 2023
@rib rib unpinned this issue Feb 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking A patch that introduces breaking changes (or an issue requiring such patch) bug 🐛 help wanted ❤️
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants