如何调试:应用程序中的JNI DETECTED ERROR:使用无效的jobject

我正在开发一个Xamarin Android项目,我收到以下错误( 这里有完整的日志)

 11-07 08:28:09.067: A/art(7164): art/runtime/java_vm_ext.cc:410] JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xd4fd90e0 11-07 08:28:09.067: A/art(7164): art/runtime/java_vm_ext.cc:410] "Thread-1973" prio=10 tid=26 Runnable 11-07 08:28:09.067: A/art(7164): art/runtime/java_vm_ext.cc:410] | group="main" sCount=0 dsCount=0 obj=0x137270a0 self=0xc89d4900 11-07 08:28:09.067: A/art(7164): art/runtime/java_vm_ext.cc:410] | sysTid=9034 nice=-11 cgrp=default sched=0/0 handle=0xd4b3a930 11-07 08:28:09.067: A/art(7164): art/runtime/java_vm_ext.cc:410] | state=R schedstat=( 310795035 15833156 94 ) utm=24 stm=7 core=5 HZ=100 11-07 08:28:09.067: A/art(7164): art/runtime/java_vm_ext.cc:410] | stack=0xd4a3c000-0xd4a3e000 stackSize=1022KB 11-07 08:28:09.067: A/art(7164): art/runtime/java_vm_ext.cc:41n0] | held mutexes= "mutator lock"(shared held) 

在飞行模式下尝试播放电影(自定义第三方ExoPlayer包装库)时。 我没有寻求帮助只用这个信息找到错误,但只是一种调试应用程序的方法。 出现崩溃时,调试器将断开连接。

另外我在Xamarin bugzilla上看过这个post: https ://bugzilla.xamarin.com/show_bug.cgi ? id = 45281,但是当我使用以下命令启用GC日志时:

 $ adb shell setprop debug.mono.log gref,gc 

该应用程序不会崩溃!

我正在测试设备Samsung SM-G930F aka Samsung S7并使用API level 23 。 该错误也出现在其他设备上。

我的构建设置:

 Xamarin Studio Community Version 6.1.1 (build 15) Installation UUID: b3096ed4-0118-4e0d-87f4-a1fe79ffc301 Runtime: Mono 4.6.1 (mono-4.6.0-branch-c8sr0/ef43c15) (64-bit) GTK+ 2.24.23 (Raleigh theme) Package version: 406010005 NuGet Version: 3.4.3.0 Xamarin.Profiler Not Installed Apple Developer Tools Xcode 8.1 (11544) Build 8B62 Xamarin.Mac Version: 2.10.0.105 (Xamarin Studio Community) Xamarin.iOS Version: 10.0.1.10 (Xamarin Studio Community) Hash: ad1cd42 Branch: cycle8-sr0-xi Build date: 2016-10-03 15:18:44-0400 Xamarin.Android Version: 7.0.1.3 (Xamarin Studio Community) Android SDK: /Users/andi/Library/Android/sdk Supported Android versions: 5.0 (API level 21) 6.0 (API level 23) 7.0 (API level 24) SDK Tools Version: 25.2.2 SDK Platform Tools Version: 24.0.3 SDK Build Tools Version: 23.0.1 Java SDK: /usr java version "1.8.0_91" Java(TM) SE Runtime Environment (build 1.8.0_91-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode) Android Designer EPL code available here: https://github.com/xamarin/AndroidDesigner.EPL Xamarin Android Player Version: 0.6.5 Location: /Applications/Xamarin Android Player.app Build Information Release ID: 601010015 Git revision: fa52f02641726146e2589ed86ec4097fbe101888 Build date: 2016-09-22 08:03:02-04 Xamarin addins: 75d65712af93d54dc39ae4c42b21dfa574859fd6 Build lane: monodevelop-lion-cycle8-sr0 Operating System Mac OS X 10.12.1 Darwin Pentagon.local 16.1.0 Darwin Kernel Version 16.1.0 Thu Oct 13 21:26:57 PDT 2016 root:xnu-3789.21.3~60/RELEASE_X86_64 x86_64 

编辑:

启用gref启用日志(无崩溃): https ://gist.github.com/sanandrea/b9a837b8c885ac037c4f4bc6e8030d10

没有启用gref(崩溃): https ://gist.github.com/sanandrea/d2c5c895b4bc15f45381421c9c21b859

编辑2这可以标记为#HeisenBug

理想情况下,如何调试此类情况非常接近您所遵循的路径。

您要做的第一件事是通过adb或带有AndroidEnvironment Build Actionenvironment.txt文件启用gref日志( 注意:使用后一选项有一些限制 – https://developer.xamarin.com/guides / android / advanced_topics / environment /#Overview ):

adb shell setprop debug.mono.log gref

https://developer.xamarin.com/guides/android/troubleshooting/troubleshooting/#Global_Reference_Messages

大! 现在我们可以看到各个全局引用的生命周期(简称gref)。 这是一个起点。 为了将来在这篇文章中的参考,让我们定义几个项目:

  • gref – 全球参考
  • wref – 弱全球参考

理想情况下,我们希望在物理设备上测试它,因为它将具有~52000 grefs的限制。 而模拟器的限制为2000 grefs。 如你想象的那样,如果你很快跨过这条线(你可能会这样),这可能会非常麻烦。

接下来,我们可以遵循我们想要了解的四条主要消息的惯例:

  • +g+ – gref创建开始
  • -g-开始 – gref销毁
  • +w+ – wref创建开始
  • -w-开始 – wref破坏

您可能还会注意到,在这些行上有一个grefc值。 这是指gref count ,即Xamarin.Android所做的总量。 然后,您可以假设grefwc值为wref count 。 我们在一个小表中定义它:

  • grefc – gref count
  • grefwc – wref计数

让我们来看看这个语法的一个例子:

 I/monodroid-gref(12405): +g+ grefc 108 gwrefc 0 obj-handle 0x40517468/L -> new-handle 0x40517468/L from at Java.Lang.Object.RegisterInstance(IJavaObject instance, IntPtr value, JniHandleOwnership transfer) I/monodroid-gref(12405): at Java.Lang.Object.SetHandle(IntPtr value, JniHandleOwnership transfer) I/monodroid-gref(12405): at Java.Lang.Object..ctor(IntPtr handle, JniHandleOwnership transfer) I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor..ctor(System.Action handler, Boolean removable) I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor..ctor(System.Action handler) I/monodroid-gref(12405): at Android.App.Activity.RunOnUiThread(System.Action action) I/monodroid-gref(12405): at Mono.Samples.Hello.HelloActivity.UseLotsOfMemory(Android.Widget.TextView textview) I/monodroid-gref(12405): at Mono.Samples.Hello.HelloActivity.m__3(System.Object o) I/monodroid-gref(12405): handle 0x40517468; key_handle 0x40517468: Java Type: `mono/java/lang/RunnableImplementor`; MCW type: `Java.Lang.Thread+RunnableImplementor` I/monodroid-gref(12405): Disposing handle 0x40517468 I/monodroid-gref(12405): -g- grefc 107 gwrefc 0 handle 0x40517468/L from at Java.Lang.Object.Dispose(System.Object instance, IntPtr handle, IntPtr key_handle, JObjectRefType handle_type) I/monodroid-gref(12405): at Java.Lang.Object.Dispose() I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor.Run() I/monodroid-gref(12405): at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this) I/monodroid-gref(12405): at System.Object.c200fe6f-ac33-441b-a3a0-47659e3f6750(IntPtr , IntPtr ) I/monodroid-gref(27679): +w+ grefc 1916 gwrefc 296 obj-handle 0x406b2b98/G -> new-handle 0xde68f4bf/W from take_weak_global_ref_jni I/monodroid-gref(27679): -w- grefc 1915 gwrefc 294 handle 0xde691aaf/W from take_global_ref_jni 

句柄或obj-handle值是JNI句柄值,’/’之后的字符是句柄值的类型:/ L表示本地引用,/ G表示全局引用,/ W表示弱全局引用。

现在让我们在考虑这个注意事项的情况下看看各种场景:

 # Java instance is created and wrapped by a MCW I/monodroid-gref(27679): +g+ grefc 2211 gwrefc 0 obj-handle 0x4066df10/L -> new-handle 0x4066df10/L from ... I/monodroid-gref(27679): handle 0x4066df10; key_handle 0x4066df10: Java Type: `android/graphics/drawable/TransitionDrawable`; MCW type: `Android.Graphics.Drawables.TransitionDrawable` # A GC is being performed... I/monodroid-gref(27679): +w+ grefc 1953 gwrefc 259 obj-handle 0x4066df10/G -> new-handle 0xde68f95f/W from take_weak_global_ref_jni I/monodroid-gref(27679): -g- grefc 1952 gwrefc 259 handle 0x4066df10/G from take_weak_global_ref_jni # Object is still alive, as handle != null # wref turned back into a gref I/monodroid-gref(27679): *try_take_global obj=0x4976f080 -> wref=0xde68f95f handle=0x4066df10 I/monodroid-gref(27679): +g+ grefc 1930 gwrefc 39 obj-handle 0xde68f95f/W -> new-handle 0x4066df10/G from take_global_ref_jni I/monodroid-gref(27679): -w- grefc 1930 gwrefc 38 handle 0xde68f95f/W from take_global_ref_jni # Object is dead, as handle == null # wref is freed, no new gref created I/monodroid-gref(27679): *try_take_global obj=0x4976f080 -> wref=0xde68f95f handle=0x0 I/monodroid-gref(27679): -w- grefc 1914 gwrefc 296 handle 0xde68f95f/W from take_global_ref_jni 

您可以在Xamarin.Android垃圾收集算法上看到我的其他答案,以确切了解这些句柄何时发生变化。

现在,您已经了解了在各种场景中可以看到的模式,它将帮助您了解invalid jobject时发生的情况。

现在是有趣的部分,但也可能是最难的部分:

现在,您需要在启用此日志记录时复制崩溃。

完成后,您需要收到收到的新错误消息和给您的句柄。 在您的原始post中,它指的是:

 JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xd4fd90e0 

但是,这个handle可能会在问题的不同复制过程中发生变化。 但是,一旦掌握了这个handle ,就可以使用像grep这样的工具来搜索handle字符串:

0xd4fd90e0

完成此操作后,您可以通过上面的示例代码段查看此handle所处的状态,并在相应区域中进行修复。 (过早GC,手动处理对象等)

参考: https //developer.xamarin.com/guides/android/troubleshooting/troubleshooting/#Global_Reference_Messages

$ adb shell setprop debug.mono.log gref,gc

该应用程序不会崩溃!

我正在测试设备Samsung SM-G930F aka Samsung S7并使用API​​级别23。

我完全一样!

在Android属性中,

关闭共享运行时帮助了我。

在此处输入图像描述