Skip to content

Please document that attach_current_thread_permanently() prevents automatic de-allocation of JNI local references #173

@wuwbobo2021

Description

@wuwbobo2021

I had encountered a memory leak problem while debugging the initial version of android-usbser-rs. A loop polling for USB transfer data causes the problem, and it crashed on older Android versions (< 8.0).

I did an experiement and realized that attach_current_thread_permanently() prevents automatic de-allocation of JNI local references, unless making use of local frames or the AutoLocal wrapper. Is there a solution with better performance?

Test program:

[package]
name = "android-simple-test"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
jni = "0.21"
ndk-context = "0.1"
android-activity = { version = "0.6", features = ["native-activity"] }

[lib]
name = "android_simple_test"
crate-type = ["cdylib"]
path = "main.rs"

[package.metadata.android]
package = "com.example.android_simple_test"
build_targets = [ "aarch64-linux-android" ]

[package.metadata.android.sdk]
min_sdk_version = 16
target_sdk_version = 30
#[no_mangle]
fn android_main(_: android_activity::AndroidApp) {
    let mut i: i32 = 0;
    loop {
        let _ = with_jni_env_ctx(|env, _| {
            let _ = env.new_string(format!("test {}", i))
                // .map(|o| env.auto_local(o))
                .unwrap();
            Ok(())
        });
        i = i.wrapping_add(1);
        if i == 1000_000 {
            println!("Not crashed");
        }
    }
}

use jni::errors::Error;
fn with_jni_env_ctx<R>(
    f: impl FnOnce(&mut jni::JNIEnv, &jni::objects::JObject<'static>) -> Result<R, Error>,
) -> Result<R, Error> {
    let ctx = ndk_context::android_context();
    let jvm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
    let context = unsafe { jni::objects::JObject::from_raw(ctx.context().cast()) };
    let mut env = jvm.attach_current_thread()?;
    f(&mut env, &context)
}

It causes memory leak, and crashes on older Android versions:

11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115] JNI ERROR (app bug): local reference table overflow (max=512)
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115] local reference table dump:
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]   Last 10 entries (of 512):
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       511: 0x12d511a0 java.lang.String "test 511"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       510: 0x12d51180 java.lang.String "test 510"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       509: 0x12d51160 java.lang.String "test 509"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       508: 0x12d51140 java.lang.String "test 508"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       507: 0x12d51120 java.lang.String "test 507"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       506: 0x12d51100 java.lang.String "test 506"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       505: 0x12d510e0 java.lang.String "test 505"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       504: 0x12d510c0 java.lang.String "test 504"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       503: 0x12d510a0 java.lang.String "test 503"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       502: 0x12d51080 java.lang.String "test 502"
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]   Summary:
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115]       512 of java.lang.String (512 unique instances)
11-16 01:49:21.761 16408 16422 F art     : art/runtime/indirect_reference_table.cc:115] 
11-16 01:49:21.765 16408 16421 I RustStdoutStderr: referenceTable GDEF length=814 1
11-16 01:49:21.765 16408 16421 I RustStdoutStderr: referenceTable GSUB length=11364 1
11-16 01:49:21.765 16408 16421 I RustStdoutStderr: referenceTable GPOS length=47302 1
11-16 01:49:21.778   856  1849 D audio_hw_primary: start_output_stream: exit
11-16 01:49:21.813 16408 16422 F art     : art/runtime/barrier.cc:90] Check failed: count_ == 0 (count_=-1, 0=0) Attempted to destroy barrier with non zero count
11-16 01:49:21.813 16408 16422 F art     : art/runtime/runtime.cc:366] Runtime aborting --- recursively, so no thread-specific detail!
11-16 01:49:21.813 16408 16422 F art     : art/runtime/runtime.cc:366] 
--------- beginning of crash
11-16 01:49:21.813 16408 16422 F libc    : Fatal signal 6 (SIGABRT), code -6 in tid 16422 (android_main)

Put the loop in another thread, do not attach it to the JVM permanantly, then you'll not have this problem.

PS: would you like to check this issue (not important): mzdk100/droid-wrap#2.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions