试图追查gref泄漏

kab*_*002 7 android memory-leaks xamarin.android xamarin

我有一个问题,一个应用程序在运行大约10分钟后可靠地崩溃,我正在尝试追踪崩溃的来源.

logcat似乎表明崩溃是由于基于以下行的grefs泄漏造成的.

09-14 23:36:48.055 12383 12409我monodroid-gc:46080杰出的GREF.>执行完整的GC!

此行之后是重复GC活动的狂热,直到活动管理器终止应用程序,如下一行中所示.

"08-23 11:10:14.393 880 894 I ActivityManager:Killing> 1909:WheelchairQuickCollect.WheelchairQuickCollect/u0a220(adj 0):用户请求>错误后"(注意这一行来自早期的日志 - 我只是把它放在手边)

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

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

但是我有两个日志问题

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

2)启用gref日志会导致我的应用程序的主ui冻结.我已多次复制此行为,但未能弄清楚为什么会这样.我在ide中没有任何未被捕获的例外,只是冻结屏幕,然后活动经理强制完成我的主要活动

(09-14 23:45:30.742 883 909 W ActivityManager:强制完成活动> WheelchairQuickCollect.WheelchairQuickCollect/md580d5d820f0b3cedc88e4799f6dbbf8c> 5.MainActivity)

任何人都有任何关于我如何识别锁定UI线程的建议?

还有任何关于如何确定使用所有grefs的内容的想法将不胜感激.甚至.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)
Run Code Online (Sandbox Code Playgroud)

UPDATE

非常感谢SushiHangover让我走上了正确的道路.我发现泄漏是来自android库的usb序列c#端口.几条评论.

  • 通过禁用从android传感器API请求/处理测量的一些代码,我能够避免锁定UI.不知道为什么会这样,但我现在没有时间弄明白.
  • 我在/ data/user/0/package_name_here /files/.__override__/grefs.txt中找到了gref.txt日志.这与SushiHangover的回答略有不同.不确定这是否是我正在运行的Android版本(7.1.1)的结果.我通过在主要活动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);
    }
Run Code Online (Sandbox Code Playgroud)

示例表

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
Run Code Online (Sandbox Code Playgroud)

Sus*_*ver 6

由于事情随着时间的推移而发生了变化,该gref文档有点缺乏如何获取详细信息的细节。

启用 gref 日志记录:

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

adb shell setprop debug.mono.log gref
Run Code Online (Sandbox Code Playgroud)

注意:我从未见过它挂起 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)           
~~~~
Run Code Online (Sandbox Code Playgroud)

格列夫详细信息位于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`
Run Code Online (Sandbox Code Playgroud)

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

adb pull如果您有 root 访问权限,请使用

注意:通过基于非生产构建的模拟器获得 root 访问权限abd root

adb pull /data/data/com.sushihangover.Android_Gref_Test/files/.__override__/grefs.txt ~/Desktop/grefs.txt
Run Code Online (Sandbox Code Playgroud)

或者使用root shell

adb shell 
cd /data/data/com.sushihangover.Android_Gref_Test/files/.__override__/
cat grefs.txt
Run Code Online (Sandbox Code Playgroud)

或者,如果您的应用程序包被标记为可调试:

adb shell
run-as com.sushihangover.Android_Gref_Test
cd files/.__override__
cat grefs.txt
Run Code Online (Sandbox Code Playgroud)

将其复制Grefs.txt到公共目录:

注意:这只是一个简单的连续后台线程,将其放入您的Activity.OnCreate或在一些基于调试的应用程序设置等中进行设置...

#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
Run Code Online (Sandbox Code Playgroud)

您还可以通过以下方式强制输出到不同的目录:

adb shell setprop debug.mono.log gref=/some/writable/path/grefs.txt
Run Code Online (Sandbox Code Playgroud)