有一个简单的.net .exe应用程序.一旦执行它的生命周期是什么.我的理解是发生以下情况:
> 1. OS loads exe assemblies into memory
> 2. OS checks if it is in fact .net assembly
> 3. mscoree.dll loads, and loads CLR
> 4. CLR takes over and loads external dlls, GC stuff, memory management etc.
> 5. CLR creates app domain where exe assemblies are loaded
> 6. exe is started
Run Code Online (Sandbox Code Playgroud)
鉴于以上是正确的(请随意详细说明),我对CLR加载程序集时的最后一步感兴趣.
创建了多少堆栈,堆,线程?
在应用程序存在的过程中,可能存在许多线程.但是,其中一个将是执行的主要线程.
创建线程时,会分配1 MB的堆栈.
谁分配内存(操作系统或CLR?)
正如指出这里
公共语言运行库的垃圾收集器管理应用程序的内存分配和释放
这是托管和非托管程序之间的显着差异.如果你用C或C++编程,你肯定知道这个责任属于开发人员而不属于垃圾收集器.这是一种强大的力量.但强大的力量带来了巨大的责任.您负责查找(创建)所需的内存空间.然后你在那里分配你的对象,当你不再需要它时,你必须释放这个记忆.在上述过程中可能出现的任何错误都会导致内存泄漏,甚至导致程序崩溃.更不用说对这样的bug进行故障排除的难度了.
另一方面,在托管程序(C#,Java,JavaScript等)的世界中,此责任属于称为垃圾收集器的运行时段.垃圾收集器分配内存并决定何时启动和垃圾垃圾的适当时间.它本身就是一个很大的便利,使开发人员的生活变得更加容易.然而,这是一个权衡.托管程序无法达到结构良好的非托管程序的性能.
关闭exe时会发生什么?CLR在卸载App Domain之前是否运行任何GC?(关闭exe),还是操作系统?
当您关闭可执行文件时,在卸载应用程序域之前将要完成的事情之一是垃圾收集,按照任何已用资源的释放顺序.然后将卸载应用程序域.在此之后,CLR将与进程的内存空间分离,最后进程将被终止.
嗯,这个过程本身比你写的要复杂一些。有些步骤包含的内容比人们想象的要多一些 - 例如,步骤 1 以及 PE 如何加载(以及 mscoree.dll 本身加载)包括内部步骤。
但是,我会尽力回答你的问题。请注意,您的问题有点大,所以我尝试简短地回答它们。但是,如果您真的对此感兴趣,我强烈建议您阅读 CLR via C#(作者:Richter)。他在第一章讨论了加载过程,并有专门的章节介绍垃圾收集器。
还有一些关于垃圾收集器基础知识的优秀 MSDN 文章,您可能会感兴趣。
创建了多少个栈、堆、线程?是否创建了线程并执行可执行文件中的代码?
简单(空)控制台应用程序将有 3 个线程:主线程、GC 线程和终结器线程。当然,每个线程都有自己的堆栈(每个1MB)。
堆的数量取决于您使用的 GC 类型。如果您使用 Workstation GC(默认),将会有 1 个托管堆(有 2 个段,一个用于“普通”对象,一个是大对象堆段)。
如果您使用服务器 GC,则系统中可用的每个逻辑核心将有 1 个堆(每个堆都有两个段)。
初始分配的内存大小是多少?谁分配内存(OS 还是 CLR?)
初始内存由多个元素组成:每个线程有 1MB 堆栈,进程中加载的图像的大小(当然这取决于您的应用程序),还有进程的“动态”元素size - 您在应用程序中进行的分配,导致 GC 增加堆大小,以及您不再使用的对象,这些对象被 GC 清理并可能导致 GC 释放内存。
如果 exe 运行时需要更多内存,谁来决定分配多少内存以及何时分配这些内存?
如果您有一个简单的控制台应用程序,那么您可以在 Main 中创建一个新类的实例。在这种情况下,“new”关键字(CIL“newobj”指令)将使 CLR 计算所需的内存量。
如果第 0 代中有足够的内存(存储新创建的对象),则不会有额外的内存分配。如果没有足够的内存,GC 将启动并调用 VirtualAlloc 为对象分配内存。在这种情况下,新创建的对象的引用将保存在堆栈上。
当然,保存引用的位置(堆栈、堆、处理器寄存器)和分配对象的位置(堆栈/堆)可能会有所不同。基本上,这取决于我们是否正在讨论类或结构的分配,以及分配的上下文是什么(如果它在方法内部,作为其他类中的字段,结构中的字段等)。它也可能因平台而异。
如果 exe 运行时需要更多内存,谁来决定分配多少内存以及何时分配这些内存?
进程中创建的新对象的所有内存分配均由 CLR 本身管理(当然,CLR 使用 Windows API,例如 VirtualAlloc 和 VirtualFree,并且 Windows 管理虚拟内存本身)。
当您使用“new”运算符创建应在托管堆中创建的新对象时,CLR 会计算需要分配的大小(所有字段的大小 + 锁定 ob 对象所需的一点开销并知道什么类型是的)并查看托管堆中是否有可用空间(在第 0 代中,CLR 始终保留一个指向应分配新对象的位置的指针)。如果是这样,它就会使用它。否则,如果没有足够的内存,垃圾收集就会开始,有时(取决于 gc 进程后的内存状态以及其他一些东西)CLR 会调用 VirtualAlloc 为进程分配更多内存。
关闭 exe 时会发生什么?CLR 在卸载 App Domain 之前是否运行任何 GC?(关闭exe),还是操作系统?
CLR 在卸载任何应用程序域之前运行快速 GC。这个快速GC的目的是让finalize有机会运行。关闭进程时,CLR 不需要清理内存,因为操作系统无论如何都会清理内存。
希望能帮助到你。