什么是.net应用程序的生命周期

时间:2015-11-26 19:25:19

标签: c# .net

拥有一个简单的.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

鉴于上述内容是正确的(请随意详细说明),当CLR加载程序集时,我对最后一步感兴趣。

  1. 创建了多少堆栈,堆,线程?是否创建了线程并执行可执行文件中的代码?
  2. 分配的初始内存大小是多少?谁分配内存(OS或CLR?)
  3. 它如何知道最初需要多少内存?
  4. 如果exe运行时需要更多内存,谁决定分配这个内存多少以及何时分配?
  5. 关闭exe时会发生什么?CLR在卸载App Domain之前是否运行任何GC? (关闭exe),还是操作系统?

2 个答案:

答案 0 :(得分:8)

  

创建了多少堆栈,堆,线程?

在应用程序所在的过程中,可能存在许多线程。但是,其中一个将是执行的主要线程。

创建线程时,会分配1 MB的堆栈。

  

谁分配内存(OS或CLR?)

如陈述here

  

公共语言运行库的垃圾收集器管理分配   并释放应用程序的内存

这是托管和非托管程序之间的显着差异。如果你用C或C ++编程,你肯定知道这个责任属于开发人员而不属于垃圾收集器。这是一个强大的力量。但强大的力量带来了巨大的责任。您负责查找(创建)所需的内存空间。然后你在那里分配你的对象,当你不再需要它时,你必须释放这个记忆。在上述过程中可能出现的任何错误都会导致内存泄漏,甚至导致程序崩溃。更不用说对这样的bug进行故障排除的难度了。

另一方面,在托管程序(C#,Java,JavaScript等)的世界中,此责任属于称为垃圾收集器的运行时段。垃圾收集器分配内存并决定何时启动和垃圾垃圾的适当时间。它本身就是一个很大的便利,使开发人员的生活变得更加容易。然而,这是一个权衡。托管程序无法达到结构良好的非托管程序的性能。

  

关闭exe时会发生什么?CLR是否正确运行任何GC   在卸载App Domain之前? (关闭exe),还是操作系统?

当您关闭可执行文件时,在卸载应用程序域之前将要完成的事情之一是垃圾收集,按照任何已用资源的释放顺序。然后将卸载应用程序域。在此之后,CLR将与进程的内存空间分离,最后该进程将被终止。

答案 1 :(得分:2)

嗯,这个过程本身比你写的要复杂一点。一些步骤包括多个人可能想到的 - 例如,步骤1以及PE如何加载(以及mscoree.dll本身加载)包括内部步骤。

但是,我会尽力回答你的问题。请注意你的问题有点大,所以我试着简单回答一下。但是,如果你真的对此感兴趣,我强烈建议你阅读 CLR通过C#(由Richter)。他在第一章讨论了加载过程,并专门介绍了垃圾收集器。

还有一些关于垃圾收集器基础知识的MSDN文章,你可能会感兴趣。

  

创建了多少堆栈,堆,线程?是否创建了线程并执行可执行文件中的代码?

简单(空)控制台应用程序将有3个线程:主线程,GC线程和终结器线程。当然,每个线程都有自己的堆栈(每个1MB)。

堆的数量取决于您使用的GC类型。如果您正在使用Workstation GC(默认值),则将有1个托管堆(具有2个段,一个用于“普通”对象,一个是大对象堆段)。

如果您正在使用Server GC,系统中可用的每个逻辑核心将有1个堆(每个堆有两个段)。

  

分配的初始内存大小是多少?谁分配内存(OS或CLR?)

初始内存由多个元素组成:每个线程有1MB堆栈,在进程中加载​​的图像大小(当然取决于您的应用程序),并且存在“动态”大小的元素 - 您在应用程序中进行的分配,导致GC增加堆大小,以及您不再使用的对象,由GC清除并可能导致GC释放内存。

  

如果exe运行时需要更多内存,谁决定分配这个内存多少以及何时分配?

如果你有一个简单的控制台应用程序,那么你可以在Main中创建一个新类的实例。在这种情况下,“new”关键字(CIL“newobj”指令)将使CLR计算所需的内存量。

如果第0代(存储新创建的对象的位置)有足够的内存,则不会有额外的内存分配。如果内存不足,GC将启动并调用VirtualAlloc为对象分配内存。在该场景中,新创建对象的引用将保存在堆栈中。

当然,保存引用的位置(堆栈,堆,处理器寄存器)和分配对象的位置(堆栈/堆)可能会有所不同。基本上,这取决于我们是在谈论类或结构的分配,以及分配的上下文(如果它在内部方法中,作为其他类中的字段,结构中的字段等)。它也可能因平台而异。

  

如果exe运行时需要更多内存,谁决定分配这个内存多少以及何时分配?

在您的流程中创建的新对象的所有内存分配都由CLR本身管理(当然,CLR使用Windows API,如VirtualAlloc和VirtualFree,以及管理虚拟内存的窗口)。

当您使用“new”运算符创建应在托管堆中创建的新对象时,CLR会计算分配所需的大小(所有字段的大小+锁定ob对象所需的一点开销)知道它是什么类型)并查看托管堆中是否有可用空间(在第0代中,CLR始终保留指向应分配新对象的位置的指针)。如果是这样,它就会使用它。否则,如果没有足够的内存,垃圾收集开始,有时(取决于gc进程后的内存状态,以及其他一些东西)CLR将调用VirtualAlloc为进程分配更多内存。

  

关闭exe时会发生什么?CLR在卸载App Domain之前是否运行任何GC? (关闭exe),还是操作系统?

CLR在卸载任何app-domain之前运行快速GC。这个快速GC的目的是让最终版本有机会运行。关闭进程时,CLR不需要清理内存,因为操作系统无论如何都会这样做。

希望它有所帮助。