CLR探索应用程序域世界(上):Windbg SOS剖析揭示域世界
在CLR的世界中,有一系列的令人Amazing的技术和架构。其中,CLR对应用程序在内存中内存分配,执行模型,程序之间的交互等一系列的技术,值得每一个致力于DotNet平台的技术人员深究。
编程人员在开发的过程中,如果把程序集的加载(Assemblies Load),反射(Reflection),寄宿(Hosting),应用程序域(AppDomain),这四种技术结合起来使用的话,不仅能更好的使用CLR这个平台提供的强大的功能,而且能够构建更安全,更健壮的应用程序代码。
这篇博文里,就是使用托管代码的动态调试工具,来研究一下CLR内部AppDomain的世界。
首先,从一个C#程序开始:
class Program
{
static void Main(string[] args)
{
Program b = new Program();
b.test();
System.Console.ReadLine();
}
public void test()
{
int i = 67;
System.Console.WrITeLine((char)i);
System.Console.WrITeLine((char)67);
i = 1;
}
}
运行了这个应用程序以后,我们打开windbg,attach到这个托管进程。
.load SOS
加载SOS扩展调试模块,可以使用.chain指令查看加载是否正确。
0:003> lm
start end module name
00400000 00408000 TestConcoleApp (deferred)
76990000 76acd000 ole32 (deferred)
77be0000 77c38000 msVCrt (deferred)
省略若干
77fc0000 77fd1000 Secur32 (deferred)
78130000 781cb000 MSVCR80 (deferred)
79000000 79045000 mscoree (deferred)
79060000 790b3000 mscorjIT (deferred)
790c0000 79b90000 mscorlib_ni (deferred)
79e70000 7a3d6000 mscorwks (deferred)
查看下意境加载了的模块,然后使用ld命令把我们的调试符号文件载入。VS在编译生成一个Console App的时候,在debug模式的时候会在debug的bin目录下生成一个和应用程序同名的pdb文件。我们要做的,就是载入这个文件:
0:003> ld TestConcoleApp
WARNING: Unable to verify checksum for G:/Projects/TestConcoleApp/TestConcoleApp/bin/Debug/TestConcoleApp.exe
Symbols loaded for TestConcoleApp
有一个警告,咋这里先不管,再使用lm查看已经加载了的模块的时候,可以看到这个module的调试符号文件已经被加载上了。
此时,我们可以查看下Excute Engine (CLR)的堆里面都有些什么东西,我们可以使用!EEHeap命令,EE的意思,就是CLI的执行引擎,也就是我们常说的CLR。这个命令可以查看到一个托管进程里面的garbage-collected 和 Loader heaps相关信息。
0:003>!eeheap
PDB symbol for mscorwks.dll not loaded
Loader Heap:
System Domain: 7a38f918
LowFrequencyHeap: Size: 0x0(0)bytes.
HighFrequencyHeap: 00a62000(8000:1000) Size: 0x1000(4096)bytes.
StubHeap: 00a6a000(2000:1000) Size: 0x1000(4096)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: Size: 0x0(0)bytes.
DispatchHeap: Size: 0x0(0)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x2000(8192)bytes
Loader Heap 中的系统域。这个域和下面的Shared Domian一起,是对托管的宿主程序,以及托管代码不可见的。这个域加载了两个CLR执行中十分重要的Module,MSCorEE.dll和MScorwks.dll。MSCorEE.dll这个文件就是大家熟悉的shim,垫片。在CLR加载中起到的重要作用。这里就不分析了,大家可以参考别的文献的介绍。
对于每个应用程序域,都会有自己的安全描述符,安全上下文以及默认的上下文。这三个部分可以支持一个应用程序域来自定义一个单独实施的安全策略,譬如,可以用来确保宿主程序在加载托管代码的时候不会对这些重要的数据结构造成破坏。
Shared Domain: 7a38fef0
LowFrequencyHeap: 00a90000(2000:1000) Size: 0x1000(4096)bytes.
HighFrequencyHeap: Size: 0x0(0)bytes.
StubHeap: 00a9a000(2000:1000) Size: 0x1000(4096)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: 00aab000(5000:1000) Size: 0x1000(4096)bytes.
DispatchHeap: 00aa7000(4000:1000) Size: 0x1000(4096)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x4000(16384)bytes
在共享域中,加载所有的应用程序域中都要使用到的assemblies,譬如MScorlib.dll。装载了System.Object,System.ValueType这样的基础类。
Domain 1: 154250
LowFrequencyHeap: 00a70000(2000:2000) Size: 0x2000(8192)bytes.
HighFrequencyHeap: 00a72000(8000:2000) Size: 0x2000(8192)bytes.
StubHeap: Size: 0x0(0)bytes.
Virtual Call Stub Heap:
IndcellHeap: Size: 0x0(0)bytes.
LookupHeap: Size: 0x0(0)bytes.
ResolveHeap: Size: 0x0(0)bytes.
DispatchHeap: Size: 0x0(0)bytes.
CacheEntryHeap: Size: 0x0(0)bytes.
Total size: 0x4000(16384)bytes
对于特定的寄宿程序,可以根据需要创建多个应用的默认域。例如IE,ASP.NET,或者是SQLServer,可以创建一个或者是多个默认的域。域名默认情况下的name就是module的名称。
在默认域中,应用程序执行的时候需要装载经来的assemblies可以被加载到这里。
在每个应用程序域中,代码创建的对象不能直接访问另外的应用程序域中的代码。如果要访问这些代码,可以采用静态的委托,或者是appDomain的自己的方法来实现。
JIT code heap:
LoaderCodeHeap: 00db0000(10000:1000) Size: 0x1000(4096)bytes.
Total size: 0x1000(4096)bytes
对于托管的应用程序,有两种把IL代码编译成本地代码的方式。一种是在第一次运行的时候,调用JIT模块来实时编译,编译好了的本地代码就放到这里。在PE文件中的相应的代码处,就用一个指针指引CLR到这里来找相关的编译好了的本地代码。第二中是安装的时候就编译成为本地代码。
同时可以看到,JIT Heap占用很少的内存空间。
Module Thunk heaps:
Module 790c2000: Size: 0x0(0)bytes.
Module 00a72c24: Size: 0x0(0)bytes.
Total size: 0x0(0)bytes
Module Lookup Table heaps:
Module 790c2000: Size: 0x0(0)bytes.
Module 00a72c24: Size: 0x0(0)bytes.
Total size: 0x0(0)bytes
Total LoaderHeap size: 0xb000(45056)bytes
总共的loader Heap的大小大概在45kb左右。
Number of GC Heaps: 1
generation 0 starts at 0x013b1018
generation 1 starts at 0x013b100c
generation 2 starts at 0x013b1000
ephemeral segment allocation context: none
下面的这两个segment对于应用程序域的其他代码来说是read only的,所以,这两部分的空间比较小。这块经常保存的是小的segement片段。除非你是很长很长的字符串。而大的object,则保持在LOH中。GC Heap,可以有多个。每个GC Heap中,都可以有一个LOH。
而每个GC Heap的总大小=Segment占用的空间+LOH
segment begin allocated size
0014d720 790d5588 790f4b38 0x0001f5b0(128432)
013b0000 013b1000 013b3ff4 0x00002ff4(12276)
Large object heap starts at 0x023b1000
segment begin allocated size
023b0000 023b1000 023b3250 0x00002250(8784)
Total Size 0x247f4(149492)
GC Heap Size 0x247f4(149492)
总共的GC堆大概150kb。
托管线程的内存结构:
这里,简单的交代一下一个托管进程的内存结构。在创建了一个托管的应用程序的线程以后,首先默认情况下创建了最少3个应用程序域,就是系统域,共享域,和默认域。前两个对托管的用户代码来说是不可见的。当时,可以调用共享与中的assemblies。用户的托管代码,和模块被load到默认域中。一个托管的宿主可以根据需要创建一个或者是多个默认域。
托管进程,线程(hard thread,soft thread),应用程序域,程序集,模块的关系
托管进程,应用程序域,程序集,模块,这四个概念从左到右是一对多的关系。及一个托管可以对应多个应用程序域,一个appdomain可以对应多个assemblies,一个assembly可以对应多个modules。modules就是我们经常看的.exe或者是.dll。exe文件是windows下对PE文件格式扩展了的托管模块。modules也可以是托管的动态链接库文件。
对于线程,情况有点特殊。这里,首先要区别一个概念,操作系统的进程创建的线程和System.Threading.Thread这个类表示的线程。这里,把操作系统创建的进程叫做hard thread,System.Threading.Thread这个类表示的线程叫做soft Thread。hard Thread和应用程序域的关系,是多堆多的关系。就是一个应用程序域中可以存在多个hard Thread。而一个hard Thread,也可以存在于多个应用程序域里面。而soft Thread,是由应用程序域中的assemblies创建,所以,它只存在于相应的应用程序域中。
当一个系统的hard thread进入某个应用程序域中进行操作的时候,这个应用程序域就会实例化一个System.Threading.Thread类来完成这个线程对应的工作。
应用程序域的环境变量属性:
对于每个应用程序域,有一系列的Environment属性可以设置,通过设置这些属性,可以配置一个应用程序域的特性,来满足各种对于安全,性能等许多方面特别的需求。
可以参考MSDN的这里:
http://msdn2.microsoft.com/en-us/library/system.appdomain_properties.ASPx
获知应用程序域相关的所有的Properties。
特别说明:应用程序域的动态目录:
对于应用程序域的所有的属性,需要特别提到一个叫做DynamicDirectory 的属性。
这个属性的生成,有两个部分,譬如我本机上面的一个ASP.NET宿主进程的缓存文件夹:
C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NETFiles/mesapplication/8a8504fd/e1680364
C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files/mesapplication这一部分,由一个叫做DYNAMIC_BASE的属性来定义。同时包含了这个应用程序域中加载的项目文件信息。
后面的两部分8a8504fd/e1680364,根据项目文件的不同,由应用程序的APP_NAME这个属性来决定。
这样何在一起,就构成了在调试的过程中,进程在本机的缓存文件夹。
CLR探索应用程序域世界(上):Windbg SOS剖析揭示域世界的更多相关文章
- CLR探索系列:Windbg+SOS动态调试分析托管代码
http://blog.csdn.net/garyye/article/details/4788070 在使用VS进行托管应用程序的调试的时候,有的时候总感觉有些力不从心.譬如查看一个托管堆或者计 ...
- WinDbg 命令三部曲:(二)WinDbg SOS 扩展命令手册
本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载. 系列博文 <WinDbg 命令三部曲:(一)WinDbg 命令手册> <WinDb ...
- 用windbg+sos找出程序中谁占用内存过高,谁占用CPU过高(转载)
原文地址: http://www.cnblogs.com/Lawson/archive/2011/01/23/1942692.html 很早看到windbg+sos方面的知识,一直没仔细学习,也许因为 ...
- windbg sos版本不匹配问题解决
dumpheap 时提示: 0:105> !dumpheap -stat The garbage collector data structures are not in a valid sta ...
- 带你剖析WebGis的世界奥秘----Geojson数据加载(高级)(转)
带你剖析WebGis的世界奥秘----Geojson数据加载(高级) 转:https://zxhtom.oschina.io/zxh/20160819.html 编程 java 2016/08/ ...
- 带你剖析WebGis的世界奥秘----点和线的世界(转)
带你剖析WebGis的世界奥秘----点和线的世界 转:https://zxhtom.oschina.io/zxh/20160813.html 编程 java 2016/08/13 0留言, 0 ...
- 带你剖析WebGis的世界奥秘----瓦片式加载地图(转)
带你剖析WebGis的世界奥秘----瓦片式加载地图 转:https://zxhtom.oschina.io/zxh/20160805.html 编程 java 2016/08/05 0留言, ...
- 为什么我会认为SAP是世界上最好用最牛逼的ERP系统,没有之一?
为什么我认为SAP是世界上最好用最牛逼的ERP系统,没有之一?玩过QAD.Tiptop.用友等产品,深深觉得SAP是贵的有道理! 一套好的ERP系统,不仅能够最大程度承接适配企业的管理和业务流程,在技 ...
- Nivo Slider - 世界上最棒的 jQuery 图片轮播插件
Nivo Slider 号称世界上最棒的图片轮播插件,有独立的 jQuery 插件和 WordPress 插件两个版本.目前下载量已经突破 1,800,000 次!jQuery 独立版本的插件主要有如 ...
随机推荐
- bzoj3413
SAM好题,显然我们不能与每个后缀都去算LCP 考虑对询问串每一位算贡献,先构建出逆序构建自动机,这样我们得到了原串的后缀树(parent树) 根据parent树的定义,一个节点对应字符串出现的位置对 ...
- Qt之自定义界面(QMessageBox)
简述 通过前几节的自定义窗体的学习,我们可以很容易的写出一套属于自己风格的界面框架,通用于各种窗体,比如:QWidget.QDialog.QMainWindow. 大多数窗体的实现都是采用控件堆积来完 ...
- UVa 1467 (贪心+暴力) Installations
题意: 一共有n项服务,每项服务有安装的时间s和截止时间d.对于每项任务,如果有一项超出截止时间,惩罚值为所超出时间的长度.问如何安装才能使惩罚值最大的两个任务的惩罚值之和最小. 分析: 如果是求总惩 ...
- UVa 12096 The SetStack Computer【STL】
题意:给出一个空的栈,支持集合的操作,求每次操作后,栈顶集合的元素个数 从紫书给的例子 A={{},{{}}} B={{},{{{}}}} A是栈顶元素,A是一个集合,同时作为一个集合的A,它自身里面 ...
- 学习:java设计模式—工厂模式
一.工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. 工厂模式在<Java与模式>中分为三类: 1)简单工厂模式(Simple Facto ...
- erl0002-erlang ets学习笔记
ets全称“erlang term storage” erlang项式存储. ets打破了erlang“不变数据”的原则,使得进程之间可以共享数据.首先引起的思考是为什么会出现ets?下面是对网络资料 ...
- java 错误:找不到或无法加载主类的解决办法
此类错误的常见解决办法: 1.是因为.java文件不在项目的src路径内,也就是说源代码未被eclipse编译,字节码不存在无法运行了在项目名上右键 -> Builder Path -> ...
- 一天一个Java基础——泛型
这学期的新课——设计模式,由我仰慕已久的老师传授,可惜思维过快,第一节就被老师挑中上去敲代码,自此在心里烙下了阴影,都是Java基础欠下的债 这学期的新课——算法设计与分析,虽老师不爱与同学互动式的讲 ...
- css中position:relative的真正理解
其实话说一直以来也没真正去理解好position:relative的用法的真实意义. 我想很多人实实在在用的多都是position:relative和position:absolute结合起来一起用的 ...
- C#发送Email邮件(实例:QQ邮箱和Gmail邮箱)
下面用到的邮件账号和密码都不是真实的,需要测试就换成自己的邮件账号. 需要引用: using System.Net.Mail; using System.Text; using System.Net; ...