草庐IT

android - 试图追踪 gref 泄漏

coder 2023-11-26 原文

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

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

09-14 23:36:48.055 12383 12409 I monodroid-gc: 46080 outstanding GREFs. >Performing a full GC!

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

"08-23 11:10:14.393 880 894 I ActivityManager: Killing >1909:WheelchairQuickCollect.WheelchairQuickCollect/u0a220 (adj 0): user request >after error" (Note this line is from an earlier log - I just had it handy)

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

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

但是我在日志记录方面遇到了两个问题

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

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

(09-14 23:45:30.742 883 909 W ActivityManager: Force finishing activity >WheelchairQuickCollect.WheelchairQuickCollect/md580d5d820f0b3cedc88e4799f6dbbf8c>5.MainActivity )

有人对我如何识别锁定 UI 线程的内容有任何建议吗?

还有任何关于如何确定什么正在用尽所有 gref 的想法将不胜感激。甚至由这些 refs 链接的 .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 传感器 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)。在某个时间点,此目录可world 访问,因此报告了一个安全漏洞并已修补 (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

或者使用root 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

关于android - 试图追踪 gref 泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46232314/

有关android - 试图追踪 gref 泄漏的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  3. 键删除后 ruby​​ 哈希内存泄漏 - 2

    你好,我无法成功如何在散列中删除key后释放内存。当我从哈希中删除键时,内存不会释放,也不会在手动调用GC.start后释放。当从Hash中删除键并且这些对象在某处泄漏时,这是预期的行为还是GC不释放内存?如何在Ruby中删除Hash中的键并在内存中取消分配它?例子:irb(main):001:0>`ps-orss=-p#{Process.pid}`.to_i=>4748irb(main):002:0>a={}=>{}irb(main):003:0>1000000.times{|i|a[i]="test#{i}"}=>1000000irb(main):004:0>`ps-orss=-p

  4. ruby - Ruby gems 的问题(损坏?)试图让指南针在 npm 中工作 - 2

    我不是Ruby专家,但想弄清楚发生了什么,因为我试图让指南针在节点应用程序中工作,但我的Ruby似乎坏了。打字:ruby--version让我:ruby2.1.1p76(2014-02-24revision45161)[x86_64-darwin13.0]我安装了Homebrew,之前遇到过Ruby版本的问题,但它似乎已安装并且可以正常工作。但是,当我使用gem输入请求时,出现此错误:$gem-hErrorloadingRubyGemsplugin"/Users/user_dir/.rvm/gems/ruby-2.1.1@global/gems/executable-hooks-1.3

  5. ruby - rails 3.0.7 内存泄漏 - 2

    我的两个不同的Rails应用程序的内存有一些奇怪的问题。这两个应用程序都使用rails3.0.7。每个Controller请求分配20-30-50MB的内存。在生产模式下,这个数量减少到5-10。但这是同样的事情。这是两个应用程序使用的gem列表:gem'pg'gem'haml'gem'sass'gem'devise'gem'simple_form'gem'state_machine'gem"globalize3","0.1.0.beta"gem"easy_globalize3_accessors"gem'paperclip'gem'andand'关闭所有这些gem不会给我任何结果。我

  6. ruby - 为什么 split (' ' ) 试图变得(太)聪明? - 2

    我刚刚发现String#split有以下奇怪的行为:"a\tbc\nd".split=>["a","b","c","d"]"a\tbc\nd".split('')=>["a","b","c","d"]"a\tbc\nd".split(//)=>["a\tb","c\nd"]Thesource(来自2.0.0的string.c)超过200行,包含这样一段话:/*L5909*/elseif(rb_enc_asciicompat(enc2)==1){if(RSTRING_LEN(spat)==1&&RSTRING_PTR(spat)[0]==''){split_type=awk;}}后来,在

  7. ruby-on-rails - 试图在 gem 中要求 active_support - 2

    我有一个ruby​​gem,我想在包含在railsactive_support模块中的gem中使用Hash.from_xml方法。我的gemspec中有以下代码:gem.add_dependency'active_support','~>3.0.0'但是,当我在本地构建和安装gem、运行irb、需要gem时,我没有看到包含主动支持的方法?关于我做错了什么或如何调试有什么建议吗?谢谢! 最佳答案 您需要从ActiveSupport要求您需要的方法;默认情况下不添加它们。正如Yevgeniy在评论中提到的那样,如果您需要所有内容,则执行

  8. ruby-on-rails - 试图理解特定的 ruby​​ 语法 - 2

    我是Ruby和Rails的新手,在浏览各种教程时,我偶尔会遇到一些我无法理解的Ruby语法。例如,这实际上是做什么的?rootto:"welcome#index"我猜想这可能是一个名为“root”的方法,但在那之后我就迷路了。“To”不是一个符号,是吗?冒号应该在前面,如果是的话,就像在":to"中一样。这是某种形式的使用哈希的关键字参数吗?在使用ruby​​1.9.3的irb中尝试时,我无法使此语法起作用。我知道这可能是一个RTFM问题,但我什至想不出要用谷歌搜索什么。谢谢!我还在研究这个语法,deffunc(h)putsh[:to]endx={:to=>"welcome#index

  9. ruby-on-rails - 升级到 OSX Lion 后 ruby​​ 进程内存泄漏 - 2

    几周前我升级到Lion,它完全被RubyonRails环境搞砸了。我已经安装了RVM,不同的ruby​​版本,但似乎找不到解决方案……我认为这是我升级到Lion所能做的最糟糕的决定之一。它只会给我带来问题。无论如何,我已经意识到渲染我的应用程序页面(它在已部署的服务器上以及在其他机器上本地运行良好)会增加20-30mb的ruby​​进程内存,这有点疯狂。所以你可以想象一下,过了一会儿,我的ruby​​进程使用了​​2gb的内存,我的电脑就不能用了。我见过很多人在升级到Lion时遇到问题,但我一直无法为我的案例找到解决方案。有人遇到过同样的问题吗?我有什么想法可以尝试解决这个问题吗?谢谢

  10. ruby - File.realpath() 是否泄漏内存? - 2

    我在使用File.realpath()时遇到问题,留下的字符串似乎没有被垃圾收集。在我看来,这像是内存泄漏,但我无法想象这样的事情对于核心库方法来说真的是真的。考虑以下代码:defstring_test(string)putsstringendGC.startreport=MemoryProfiler.reportdos='./foo.txt'.freezestring_test(s)s=nilGC.startendreport.pretty_print这会产生(以及其他冗长的输出):Totalallocated:0bytes(0objects)Totalretained:0bytes

随机推荐