在上一篇中我分析了CoreCLR中GC的内部处理,
在这一篇我将使用LLDB实际跟踪CoreCLR中GC,关于如何使用LLDB调试CoreCLR的介绍可以看:
微软官方的文档,地址
我在第3篇中的介绍,地址
LLDB官方的入门文档,地址
源代码
本篇跟踪程序的源代码如下:
using System;using System.Runtime.InteropServices;namespace ConsoleApplication{ public class Program
{ public class ClassA { } public class ClassB { } public class ClassC { }
public static void Main(string[] args) { var a = new ClassA();
{ var b = new ClassB(); } var c = new ClassC();
GCHandle handle = GCHandle.Alloc(c, GCHandleType.Pinned);
IntPtr address = handle.AddrOfPinnedObject();
Console.WriteLine((long)address);
GC.Collect();
Console.WriteLine("first collect completed");
c = null;
GC.Collect();
Console.WriteLine("second collect completed");
GC.Collect();
Console.WriteLine("third collect completed");
}
}
}
准备调试
环境和我的第三篇文章一样,都是ubuntu 16.04 LTS,首先需要发布程序:
dotnet publish
发布程序后,把自己编译的coreclr文件覆盖到发布目录中:
复制coreclr/bin/Product/Linux.x64.Debug
下的文件到程序目录/bin/Debug/netcoreapp1.1/ubuntu.16.04-x64/publish
下。
请不要设置开启服务器GC,一来是这篇文章分析的是工作站GC的处理,二来开启服务器GC很容易导致调试时死锁。
进入调试
准备工作完成以后就可以进入调试了
cd 程序目录/bin/Debug/netcoreapp1.1/ubuntu.16.04-x64/publish
lldb-3.6 程序名称
首先设置gc主函数的断点,然后运行程序
b gc1
r
我们停在了gc1函数,现在可以用bt
来看调用来源
这次是手动触发GC,调用来源中包含了GCInterface::Collect
和JIT生成的函数
需要显示当前的本地变量可以用fr v
,需要打印变量或者表达式可以用p
现在用n
来步过,用s
来步进继续跟踪代码
进入标记阶段
在上图的位置中用s
命令即可进入mark_phase
,继续步过到下图的位置
这时先让我们看下堆中的对象,加载CoreCLR提供的LLDB插件
plugin load libsosplugin.so
插件提供的命令可以查看这里的文档
执