在运行大约10分钟后,我遇到了一个应用程序可靠崩溃的问题,并且试图跟踪崩溃的根源。

logcat似乎表明崩溃是由于基于以下行的gref泄漏引起的。



此行之后是疯狂的重复GC Activity ,直到该应用程序被 Activity 管理器终止为止,如以下行中的下一行所示。



我试图通过以下adb命令启用ref日志记录(如https://developer.xamarin.com/guides/android/troubleshooting/troubleshooting/所建议)



但是我在记录日志时遇到两个问题

1)日志条目似乎未包含任何有用的信息,以下是gref日志的示例。请注意,gref指向的对象上没有任何堆栈跟踪或类型信息,如Xamarin网站上的故障排除示例所示。

2)启用gref日志会导致我的应用程序的主ui卡住。我已经多次重复了这种行为,但无法弄清楚为什么会这样。我没有得到任何未捕获的异常(exception),只是卡住屏幕,然后由 Activity 经理强制完成我的主要 Activity



任何人都对如何识别锁定UI线程有什么建议?

同样,关于如何确定所有gref用尽的任何想法也将不胜感激。即使是.net运行时的类型信息,也可以是由这些引用链接的java对象的类型信息,都是一个不错的开始。

09-14 23:45:30.389 13759 14047 I monodroid-gref: +w+ grefc 1082 gwrefc 4 obj-handle 0x1018c6/G -> new-handle 0x2002d7/W from thread 'finalizer'(14047)

09-14 23:45:30.390 13759 14047 I monodroid-gref: +w+ grefc 1075 gwrefc 11 obj-handle 0x18ea/G -> new-handle 0x2f3/W from thread 'finalizer'(14047)

09-14 23:45:30.390 13759 14047 I monodroid-gref: -g- grefc 1075 gwrefc 11 handle 0x18ea/G from thread 'finalizer'(14047)

更新

非常感谢SushiHangover使我走上正确的道路。从那以后,我发现泄漏来自c# port of the usb serial for android library。一些评论。
  • 通过禁用一些请求/处理来自android sensor API的测量的代码,我能够避免锁定UI。不知道为什么这行得通,但我现在没有时间弄清楚。
  • 我在/data/user/0/package_name_here/files/.__override__/grefs.txt中找到了gref.txt日志。这与SushiHangover的答案有所不同。不知道这是否是我正在运行的android版本(7.1.1)的结果。我通过在我的主要 Activity onCreate方法中记录this.ApplicationContext.FilesDir.AbsolutePath的结果来确定此路径。
  • gref.txt日志非常冗长,并且没有任何摘要,因此很难确定实际泄漏的对象。如果有人知道可以汇总此文件的工具,那将非常有帮助。但是,我确实发现您可以强制dalvik缓存转储引用表的摘要(本地/全局)。要转储表,必须使用反射来调用dumpReferenceTables方法,如下所示。我在专门的任务中运行debugGlobalRefWorker以每10秒打印一次表。从转储的摘要部分,我能够快速确定泄漏对象的类型。然后,我使用gref.txt日志来了解最常分配对象的位置。幸运的是,泄漏很大,因此很容易看到泄漏源。

  • 我的转储引用表的代码
        public void debugGlobalRefWorker()
        {
            while(true)
            {
                dumpGlobalRefTable();
                Task.Delay(10000).Wait();
            }
        }
    
        //dont create these in dumpGlobalRefTable otherwise they will clutter up the gref log
        Java.Lang.Reflect.Method dumpGREFTableMethod = Java.Lang.Class.ForName("dalvik.system.VMDebug").GetDeclaredMethod("dumpReferenceTables");
        Java.Lang.Object[] args = new Java.Lang.Object[0];
    
        public void dumpGlobalRefTable()
        {
            //          Java.Lang.Class cls = Java.Lang.Class.ForName("android.os.Debug");
            //          Java.Lang.Class cls = Java.Lang.Class.ForName("dalvik.system.VMDebug");
            //      var method = cls.GetDeclaredMethod("dumpReferenceTables");
            dumpGREFTableMethod.Invoke(null,args);
        }
    

    示例表
    09-18 12:20:36.091 29146 29174 I art     : global reference table dump:
    
    09-18 12:20:36.091 29146 29174 I art     :   Last 10 entries (of 677):
    
    09-18 12:20:36.091 29146 29174 I art     :       676: 0x7106a800 java.lang.Class<android.hardware.SensorEvent>
    
    09-18 12:20:36.091 29146 29174 I art     :       675: 0x32c023c0 android.hardware.SensorEvent
    
    09-18 12:20:36.091 29146 29174 I art     :       674: 0x32c04520 android.os.Bundle
    
    09-18 12:20:36.091 29146 29174 I art     :       673: 0x32c06070 com.google.android.gms.internal.zzary
    
    09-18 12:20:36.091 29146 29174 I art     :       672: 0x7104b448 java.lang.Class<android.widget.Toast>
    ...
    
    09-18 12:20:36.091 29146 29174 I art     :   Summary:
    
    09-18 12:20:36.091 29146 29174 I art     :         1 of android.runtime.UncaughtExceptionHandler
    
    09-18 12:20:36.091 29146 29174 I art     :         2 of md57dcfd83abf19bfc45de0a46e73444d92.ServiceConnectionHelper (2 unique instances)
    
    09-18 12:20:36.092 29146 29174 I art     :         1 of md526b7ac14cffc1a788e82c7b73f3add08.GoogleApiClientConnectionCallbacksImpl
    
    09-18 12:20:36.092 29146 29174 I art     :         1 of md580d5d820f0b3cedc88e4799f6dbbf8c5.WheelchairConnectService_FLPCallbackHelper
    
    09-18 12:20:36.092 29146 29174 I art     :         1 of md5e34b7f0d2ba7321e77528f2c21447828.AndroidBaroMeasurementProvider
    
    09-18 12:20:36.092 29146 29174 I art     :         1 of md5e34b7f0d2ba7321e77528f2c21447828.AndroidMagUncalMeasurementProvider
    

    最佳答案

    gref文档在细节上缺少一些细节,因为随着时间的变化,如何获取细节。

    启用gref日志记录:

    Logcat输出(通过adb logcat -s monodroid-gref过滤):

    adb shell setprop debug.mono.log gref
    

    注意:我从未见过它挂起UI线程或导致终止,但是请在运行应用程序之前尝试启用它
    ~~~~
    09-14 23:40:19.656  4053  4053 I monodroid-gref: +g+ grefc 291 gwrefc 0 obj-handle 0x100019/I -> new-handle 0x100786/G from thread '(null)'(1)
    ~~~~
    

    Gref详细信息在grefs.txt中:
    +g+ grefc 291 gwrefc 0 obj-handle 0x100019/I -> new-handle 0x100786/G from thread '(null)'(1)
      at Android.Runtime.AndroidObjectReferenceManager.CreateGlobalReference (Java.Interop.JniObjectReference value) [0x00000] in <05a8ec13540f42fc8ec62ef099f948d3>:0
      at Java.Interop.JniObjectReference.NewGlobalRef () [0x00000] in <548a126e175845e0999036cd7abdeb57>:0
      at Android.Runtime.JNIEnv.NewGlobalRef (System.IntPtr jobject) [0x00000] in <05a8ec13540f42fc8ec62ef099f948d3>:0
      at Java.Lang.Object.RegisterInstance (Android.Runtime.IJavaObject instance, System.IntPtr value, Android.Runtime.JniHandleOwnership transfer, System.IntPtr& handle) [0x00000] in <05a8ec13540f42fc8ec62ef099f948d3>:0
      at Java.Lang.Object.SetHandle (System.IntPtr value, Android.Runtime.JniHandleOwnership transfer) [0x00000] in <05a8ec13540f42fc8ec62ef099f948d3>:0
      at Java.Lang.Object..ctor () [0x00000] in <05a8ec13540f42fc8ec62ef099f948d3>:0
      at Android_Gref_Test.JavaObjectWrapper..ctor () [0x00000] in /Users/Sushi/code/Projects/Android_Gref_Test/Android_Gref_Test/MainActivity.cs:45
      at Android_Gref_Test.MainActivity.Button_Click (System.Object sender, System.EventArgs e) [0x0003b] in /Users/Sushi/code/Projects/Android_Gref_Test/Android_Gref_Test/MainActivity.cs:33
      at Android.Views.View+IOnClickListenerImplementor.OnClick (Android.Views.View v) [0x00000] in <05a8ec13540f42fc8ec62ef099f948d3>:0
      at Android.Views.View+IOnClickListenerInvoker.n_OnClick_Landroid_view_View_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_v) [0x00000] in <05a8ec13540f42fc8ec62ef099f948d3>:0
      at System.Object.cf56947b-b824-4d0d-839a-1d6dd87a5b7c (System.IntPtr , System.IntPtr , System.IntPtr ) [0x00000] in <896ad1d315ca4ba7b117efb8dacaedcf>:0
    handle 0x100786; key_handle 0x2ef9890: Java Type: `md505d171be8e81cafbccfb3a52eeebc2c5/JavaObjectWrapper`; MCW type: `Android_Gref_Test.JavaObjectWrapper`
    

    这些详细信息位于应用程序数据文件(files/.__override__/grefs.txt)中的隐藏目录中。在某个时间点,此目录可在世界范围内访问,因此是一个已报告的安全漏洞,并且已被修补(Xamarin.Android 5.1.x),因此您现在需要应用程序级别或root用户访问权限才能获取它。同样,如果写入过多,Android也会删除logcat消息,因此logcat中的gref列表可能会被删除,因此详细信息存储在单独的文件中。

    如果您具有root访问权限,请使用adb pull
    注意:通过abd root与基于非生产版本的模拟器获得root访问权限。
    adb pull /data/data/com.sushihangover.Android_Gref_Test/files/.__override__/grefs.txt ~/Desktop/grefs.txt
    

    或使用根 shell :
    adb shell
    cd /data/data/com.sushihangover.Android_Gref_Test/files/.__override__/
    cat grefs.txt
    

    或者,如果您的应用程序包被标记为可调试:
    adb shell
    run-as com.sushihangover.Android_Gref_Test
    cd files/.__override__
    cat grefs.txt
    

    Grefs.txt复制到公共(public)目录:

    注意:这只是一个简单的连续后台线程,将其放入您的Activity.OnCreate或在某些基于Debug的应用程序设置中进行设置,等等。
    #if DEBUG
        Task.Run(async () =>
        {
            const int seconds = 30;
            const string grefTag = "monodroid-gref";
            const string grefsFile = "grefs.txt";
            while (true)
            {
                var appDir = Application.ApplicationInfo.DataDir;
                var grefFile = Path.Combine("/data/data", PackageName, "files/.__override__", grefsFile);
                var grefFilePublic = Path.Combine(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDownloads).AbsolutePath, grefsFile);
                if (File.Exists(grefFile))
                {
                    File.Copy(grefFile, grefFilePublic, true);
                    Log.Debug(grefTag, $"adb pull {grefFilePublic} {grefsFile}");
                }
                else
                    Log.Debug(grefTag, "no grefs.txt found, gref logging enabled? (adb shell setprop debug.mono.log gref)");
                await Task.Delay(seconds * 1000);
            }
        });
    #endif
    

    您还可以通过以下方式将输出强制到其他目录:
    adb shell setprop debug.mono.log gref=/some/writable/path/grefs.txt
    

    10-07 11:59