【问题标题】:Triggering garbage collection in Mono在 Mono 中触发垃圾收集
【发布时间】:2012-02-04 13:28:44
【问题描述】:

如何让 Mono 中的垃圾收集器做任何有用的事情?这篇文章的底部是一个简单的 C# 测试程序,它生成两个大字符串。生成第一个字符串后,解引用变量,退出作用域,手动触发垃圾收集器。尽管如此,使用的内存并没有减少,并且程序在构造第二个字符串期间因内存不足异常而爆炸。

未处理的异常:OutOfMemoryException [ERROR] FATAL UNHANDLED 异常:System.OutOfMemoryException:内存不足(包装 托管到本机)字符串:InternalAllocateStr (int) at System.String.Concat (System.String str0, System.String str1) [0x00000] in :0 at GCtest.Main (System.String[] args) [0x00000] in :0

经过研究,我发现 Mono 的 --gc=sgen 开关使用不同的垃圾收集算法。更糟糕的是,会生成以下堆栈跟踪:

堆栈跟踪:

at (wrapper managed-to-native) string.InternalAllocateStr (int) at string.Concat (string,string) at GCtest.Main (string[]) at (wrapper runtime-invoke) .runtime_invoke_void_object(对象,intptr,intptr,intptr)

本机堆栈跟踪:

0 单基因 0x000bc086 mono_handle_native_sigsegv + 422 1 单声道
0x0000466e mono_sigsegv_signal_handler + 334 2 libsystem_c.dylib
0x913c659b _sigtramp + 43 3 ???
0xffffffff 0x0 + 4294967295 4 单发电机
0x0020306d mono_gc_alloc_obj_nolock + 363 5 mono-sgen
0x0020394a mono_gc_alloc_string + 153 6 mono-sgen
0x001c9a10 mono_string_new_size + 147 7 单声道
0x0022a6d1 ves_icall_System_String_InternalAllocateStr + 28 8 ???
0x004c450c 0x0 + 4998412 9 ???
0x004ceec4 0x0 + 5041860 10 ???
0x004c0f74 0x0 + 4984692 11 ???
0x004c1163 0x0 + 4985187 12 单基因
0x00010164 mono_jit_runtime_invoke + 164 13 mono-sgen
0x001c5791 mono_runtime_invoke + 137 14 mono-sgen
0x001c7f92 mono_runtime_exec_main + 669 15 mono-sgen
0x001c72cc mono_runtime_run_main + 843 16 单声道
0x0008c617 mono_main + 8551 17 mono-sgen
0x00002606 开始 + 54 18 ???
0x00000003 0x0 + 3

来自 gdb 的调试信息:

/tmp/mono-gdb-commands.2aCwlD:1:源命令文件中的错误:无法 自我调试

在执行本机代码时获得了 SIGSEGV。这通常表示致命 单声道运行时或您使用的本机库之一中的错误 应用。

/Users/fraser/Documents/diff-match-patch/csharp/GCtest.command: 行 12:41011 中止陷阱:6 单声道--gc=sgen GCtest.exe

代码如下:

using System;
public class GCtest {
  public static void Main(string[] args) {
    Console.WriteLine("Memory: " + (GC.GetTotalMemory(true) / 1024) + " KB");

    {
      // Generate the first string.
      string text1 = "hello old world.";
      for (int i = 0; i < 25; i++) {
        text1 = text1 + text1;
      }
      // Dereference variable.
      text1 = null;
      // Drop out of scope.
    }

    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine("Memory: " + (GC.GetTotalMemory(true) / 1024) + " KB");

    // Generate the second string.
    string text2 = "HELLO NEW WORLD!";
    for (int i = 0; i < 25; i++) {
      text2 = text2 + text2;
    }

    Console.WriteLine("Memory: " + (GC.GetTotalMemory(true) / 1024) + " KB");
  }
}

【问题讨论】:

  • 奇怪地使用了“dereference”这个词。取消引用是跟随指向其目的地的指针的过程。我会说“将 null 分配给变量”。

标签: c# mono garbage-collection


【解决方案1】:

那是 32 位单声道吗?我相信您所描述的行为是由以下事实引起的,即使用 Boehm GC,至少对堆栈进行了保守扫描。这意味着它上面的值被视为指针。如果一些这样的值指向一个对象(或它的下级),那么这个对象将不会被收集。现在很清楚,为什么大对象在这里有问题——它们很容易填充 32 位进程的虚拟地址空间,我们很有可能堆栈中的某些值指向其中的某个位置,而整个对象都没有被收集。这种讨厌的假指针的来源是什么?我最熟悉的是哈希、随机值或日期/时间(通常ints 很低)。

如何让 Mono 中的垃圾收集器做任何有用的事情?

您使用的方法是正确的,但我认为您遇到了上述问题。通常的程序不会受到太大影响,因为(所以)巨大的物体非常罕见。明天我也会在 64bit Mono 上测试它。

尽管如此,自动出现的问题是为什么 Mono 项目不进行另一个 GC,这不会是保守的?如您所见,这种垃圾收集器是 sgen。我认为 sgen 的目的是,除了 精确 收集器之外,它还可以压缩,这对于长时间运行的应用程序非常重要,并且它具有(有时)更好的性能。然而 sgen 仍处于测试阶段,人们可以到处观察崩溃。此外,在 Mono 的不同版本中,精确堆栈扫描的功能已被打开和关闭,有时会出现一些回归,因此您可能会发现 Mono 的旧版本比新版本的效果更好。尽管如此,sgen 仍在积极开发中(因为可以在 github 上找到浏览提交历史记录),并且应该很快取代 Mono 中的默认垃圾收集器。最终应该可以解决所描述的问题。

顺便说一句,例如,我的 Mono 版本(仍然是 32 位)使用 sgen 通过了这个测试:

$ mono --gc=sgen GCTest.exe 
Memory: 4098 KB
Memory: 4140 KB
Memory: 1052716 KB

希望这对您有所帮助,如果有不清楚的地方请询问(尤其是由于我的英语水平不高)。

编辑:

在我的 64 位机器上,Boehm 运行良好:

$ mono GCTest.exe
Memory: 132 KB
Memory: 280 KB
Memory: 1048860 KB

(sgen 自然也是)。它是 Linux 上的 Mono 2.10.5。

【讨论】:

  • Mono JIT 编译器版本 2.10.6(tarball Fri Sep 16 00:13:06 EDT 2011) 版权所有 (C) 2002-2011 Novell, Inc、Xamarin, Inc 和贡献者。 www.mono-project.com TLS:正常 SIGSEGV:正常 通知:kqueue 架构:x86 已禁用:无 杂项:调试器 softdebug LLVM:是(2.9svn-mono) GC:包括 Boehm(带有类型化 GC)
  • 是的,这是 32 位单声道(64 位将显示架构:amd64)。使用 64 位可以降低出现此类 GC 问题的可能性(请参阅我的答案的编辑),但 sgen 在这里是正确的解决方案。您可以尝试升级/降级 Mono(也可能发布错误)或等待 sgen 将被标记为稳定的版本。您也可以尝试一些现实世界的应用程序,您最好不要遇到此类问题(因为缺少巨大的对象)。如果它是从一些实际程序中提取的问题,也许应该重新设计。 (还有一些其他的解决方法,比如分块大对象)。
猜你喜欢
  • 2015-04-12
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多