ネイティブC(NDK r10d)からの画像を処理するAndroidアプリケーションを作成しています。 JNIにより厳格な最近のARTの紹介まで、コードはうまく機能していました。したがって、コードはDalvik(たとえば、Lolipop以前のデバイス)で正常に機能していますが、iiは最新の電話でSIGENVを作成します。
エラーが発生しました:
04-26 16:18:34.169: E/art(21443): 0xb4a2dd00 SpaceTypeMallocSpace begin=0x12c00000,end=0x12e01000,limit=0x32c00000,size=2MB,capacity=192MB,non_growth_limit_capacity=512MB,name="main rosalloc space"]
04-26 16:18:34.170: E/art(21443): 0xb4ae5640 allocspace main rosalloc space live-bitmap 3[begin=0x12c00000,end=0x32c00000]
04-26 16:18:34.170: E/art(21443): 0xb4ae5660 allocspace main rosalloc space mark-bitmap 3[begin=0x12c00000,end=0x32c00000]
04-26 16:18:34.170: E/art(21443): 0xb4874120 SpaceTypeImageSpace begin=0x6f5ab000,end=0x6ff21e58,size=9MB,name="/data/dalvik-cache/arm/system@[email protected]"]
04-26 16:18:34.170: E/art(21443): 0xb4875220 imagespace /data/dalvik-cache/arm/system@[email protected] live-bitmap 0[begin=0x6f5ab000,end=0x6ff21f00]
04-26 16:18:34.170: E/art(21443): 0xb4875220 imagespace /data/dalvik-cache/arm/system@[email protected] live-bitmap 0[begin=0x6f5ab000,end=0x6ff21f00]
04-26 16:18:34.170: E/art(21443): 0xb49d9dd0 SpaceTypeZygoteSpace begin=0x72f09000,end=0x740c7000,size=17MB,name="Zygote space"]
04-26 16:18:34.170: E/art(21443): 0xb4875440 allocspace zygote / non moving space live-bitmap 0[begin=0x72f09000,end=0x740c7000]
04-26 16:18:34.170: E/art(21443): 0xb4875460 allocspace zygote / non moving space mark-bitmap 0[begin=0x72f09000,end=0x740c7000]
04-26 16:18:34.170: E/art(21443): 0xb4a2dc80 SpaceTypeMallocSpace begin=0x740c7000,end=0x740d6000,limit=0x76f09000,size=60KB,capacity=46MB,non_growth_limit_capacity=46MB,name="non moving space"]
04-26 16:18:34.170: E/art(21443): 0xb4ae5460 allocspace non moving space live-bitmap 4[begin=0x740c7000,end=0x76f09000]
04-26 16:18:34.170: E/art(21443): 0xb4ae53c0 allocspace non moving space mark-bitmap 4[begin=0x740c7000,end=0x76f09000]
04-26 16:18:34.170: E/art(21443): 0xb486d340 large object space:GcRetentionPolicyAlwaysCollect
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] JNI DETECTED ERROR IN APPLICATION: jarray is an invalid stack indirect reference table or invalid reference: 0x740c9268 (0xdead4321)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] in call to GetByteArrayElements
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] from boolean com.googlecode.leptonica.Android.Pix.nativeGetData(int, byte[])
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] "main" prio=5 tid=1 Runnable
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] | group="main" sCount=0 dsCount=0 obj=0x72f09000 self=0xb4827800
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] | sysTid=21443 Nice=0 cgrp=default sched=0/0 handle=0xb6f6abec
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] | state=R schedstat=( 427402282 63106827 397 ) utm=28 stm=14 core=3 HZ=100
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] | stack=0xbe5e3000-0xbe5e5000 stackSize=8MB
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] | held mutexes= "mutator lock"(shared held)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #00 pc 00004e64 /system/lib/libbacktrace_libc++.so (UnwindCurrent::Unwind(unsigned int, ucontext*)+23)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #01 pc 00003665 /system/lib/libbacktrace_libc++.so (Backtrace::Unwind(unsigned int, ucontext*)+8)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #02 pc 00256429 /system/lib/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, int, char const*, art::mirror::ArtMethod*)+84)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #03 pc 00238fe7 /system/lib/libart.so (art::Thread::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&) const+158)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #04 pc 000b191b /system/lib/libart.so (art::JniAbort(char const*, char const*)+610)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #05 pc 000b2055 /system/lib/libart.so (art::JniAbortF(char const*, char const*, ...)+68)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #06 pc 000b4455 /system/lib/libart.so (art::ScopedCheck::Check(bool, char const*, ...) (.constprop.129)+480)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #07 pc 000bee03 /system/lib/libart.so (art::CheckJNI::GetByteArrayElements(_JNIEnv*, _jbyteArray*, unsigned char*)+62)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #08 pc 00239478 /data/app/com.bill2bin.core.lib.demo-1/lib/arm/liblept.so (_JNIEnv::GetByteArrayElements(_jbyteArray*, unsigned char*)+48)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #09 pc 0023992c /data/app/com.bill2bin.core.lib.demo-1/lib/arm/liblept.so (Java_com_googlecode_leptonica_Android_Pix_nativeGetData+540)
04-26 16:18:34.263: A/art(21443): art/runtime/check_jni.cc:65] native: #10 pc 0008d3b5 /data/dalvik-cache/arm/data@[email protected]@[email protected] (Java_com_googlecode_leptonica_Android_Pix_nativeGetData__I_3B+104)
04-26 16:18:34.264: A/art(21443): art/runtime/check_jni.cc:65] at com.googlecode.leptonica.Android.Pix.nativeGetData(Native method)
04-26 16:18:34.264: A/art(21443): art/runtime/check_jni.cc:65] at com.googlecode.leptonica.Android.Pix.getData(Pix.Java:94)
04-26 16:18:34.264: A/art(21443): art/runtime/check_jni.cc:65] at com.bill2bin.core.lib.demo.VideoPipeDebug.testDoJNIDebug(VideoPipeDebug.Java:449)
04-26 16:18:34.264: A/art(21443): art/runtime/check_jni.cc:65] at com.bill2bin.core.lib.demo.CameraActivity.runTest1(CameraActivity.Java:133)
Javaで実行するコードは次のとおりです。
/**
* Return the raw bytes of the native PIX object. You can reconstruct the
* Pix from this data using createFromPix().
*
* @return a copy of this PIX object's raw data
*/
public byte[] getData() {
int size = nativeGetDataSize(mNativePix);
// Size is usually quite big since I work on pictures (1Mo-300Ko)
byte[] buffer = new byte[size];
if (!nativeGetData(mNativePix, buffer)) {
throw new RuntimeException("native getData failed");
}
return buffer;
}
private static native boolean nativeGetData(long nativePix, byte[] data);
対応するネイティブコードは次のとおりです。
jboolean Java_com_googlecode_leptonica_Android_Pix_nativeGetData(JNIEnv *env,
jclass clazz, jlong nativePix, jbyteArray data) {
PIX *pix = (PIX *) nativePix;
jbyte *data_buffer = env->GetByteArrayElements(data, NULL);
l_uint8 *byte_buffer = (l_uint8 *) data_buffer;
size_t size = 4 * pixGetWpl(pix) * pixGetHeight(pix);
memcpy(byte_buffer, pixGetData(pix), size);
env->ReleaseByteArrayElements(data, data_buffer, 0);
return JNI_TRUE;
}
GetByteArrayElementsがエラーの原因であるように見えますが、JNIEnv参照とjbyteArrayはAndroidによって提供されており、保存も変更もしていません。バッファ配列は常に同じJavaスレッドに割り当てられるため、どのように破損するかわかりません...かなり困惑しています:)
この問題の原因は何ですか?
ヒープが小さすぎますか?それともARTの問題なのでしょうか(本当に疑問ですが...)?
ご協力ありがとうございます!
Alex Cohnのアドバイスに従って、次のコードを機能させました。
Java
public byte[] getData() {
byte[] buffer = nativeGetData(mNativePix);
if (buffer == null) {
throw new RuntimeException("native getData failed");
}
return buffer;
}
private static native byte[] nativeGetData(long nativePix);
ネイティブ
jbyteArray Java_com_googlecode_leptonica_Android_Pix_nativeGetData(
JNIEnv *env, jclass clazz, jlong nativePix) {
PIX *pix = (PIX *) nativePix;
// Get the size
size_t size = 4 * pixGetWpl(pix) * pixGetHeight(pix);
jbyteArray result = env->NewByteArray(size);
if (result == NULL) {
LOGE("Cannot allocate JNI Byte Array");
return NULL; /* out of memory error thrown */
}
// move from the Pix to the Java structure
env->SetByteArrayRegion(result, 0, size,(jbyte*)pixGetData(pix));
return result;
}
ありがとう!
これは、現在のスレッド内の現在のネイティブメソッドの期間中有効であることを意味します。ネイティブメソッドが戻った後もオブジェクト自体が存続している場合でも、参照は無効です。
Jclass/jarray宣言でこれを置き換えてみてください。
jclass localClass = env->FindClass("MyClass");
jclass globalClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
JNI Tips を参照してください
私は同じ問題に直面しました。 JavaからjbyteArrayを取得してC関数を作成しました。これは、他の既存の機能や動作中の機能に非常に似ていました。しかし、削除されたずっと前のオブジェクトへのアクセス、50レコードなどのテーブルのレコード番号〜6000へのアクセスに関する恐ろしいメッセージで激しくクラッシュしました...コードを最小限に簡素化して、それに渡されました。呼び出し元のスレッドに関係なく、この配列の形成方法に関係なく。署名とできることをすべてチェックしました。そこから印刷してログに記録できるため、関数は確実に呼び出されていました。
このトピックを読んだ後、私は運命を感じました:)私は間違いなく私のC関数内でデータを生成できませんでした。
何が助けになったのか:ソースファイルのわずかに異なる部分で、この関数を手動で(コピーアンドペーストなしで)書き換えました。また、念のため名前を変更しました。古い関数の本体が削除されました。そして、すぐに適切に機能し始めました。
私はそれが何だったのかわかりませんが、純粋なC言語とPerl言語で私の人生で似たような状況に何度か直面しました。
Android Studio。 AOSP 7.1.2フォーク。