.net应用程序运行的背后机制

这篇文章要探讨的问题是:当编译后的应用程序运行时,CLR是如何进行运作的。

1. 准备工作

程序清单Program.cs:

public sealed class Pragram
{
	public static void Main()
	{
		System.Console.WriteLine("Hi");
	}
}

在Developer Command Prompt for VS2013中编译上面的代码成为应用程序(程序集)

csc.exe Program.cs

运行,程序开始执行。

Program.exe

用ILDasm打开编译的程序集

ILDasm.exe  Program.exe

程序就可以运行了。

2. CLR加载并初始化自身

通过双击Program.exe或在CMD中运行程序时,Window会检查EXE文件头,决定创建32位还是64位进程之后,会在进程地址空间加载MSCorEE.dll的x86,x64或ARM版本。

如果操作系统是x86或ARM版本,MSCorEE.dll的x86在以下目录中

%SystemRoot%\System32

如果操作系统是x64,MSCorEE.dll的x86版本在以下目录中

%SystemRoot%\SysWow64

MSCorEE.dll的x64版本则在以下目录中

%SystemRoot%\System32

然后,进程的主线程调用MSCorEE.dll中定义的一个方法,这个方法会初始化CLR。之后权限就交给CLR了

3. 从CLR头读取入口点

CLR初始化完成后,它会读取CLR头,查找应用程序入口标记

我们可以用ILDasm(view→headers)来查看程序入口标记。

在ILDasm,View→metaInfo→show打开元数据信息窗口程序的入口点为:0x06000001。06表示标记的类型为MethodDef,000001表示是MethodDef表的第一行。之后通过这个方法定义表的标记,检索MethodDef元数据表。

根据RVA就找到方法在IL代码中的偏移量。

下面我们查看一下Main函数的IL代码

IL代码如下:

4. CLR检测入口点方法的代码所引用的类型及方法

在Main执行前,会根据入口函数的所引用的类型和成员引用,加载它们的定义程序集(如果没有加载的话)。例如,上述IL代码包含对System.Console.WriteLine的引用。具体的说,IL call指令引用了元数据token 0A000003,该token表示MemberRef元数据表(表0A)中的记录项3。CLR检查该MembersRef记录项,发现它的字段引用了TypeRef表中的记录项01000004。根据这个TypeRef项,CLR被引导至一个AssemblyRef记录项(23000001):

这时,就知道了它需要的是哪个程序集,接着,CLR就会定位被加载该程序集。

5. 加载引用类型程序集并在内存中创建数据结构

CLR加载mscorlib.dll文件,并扫描元数据来定位Console类型。然后,CLR创建它的内部数据结构来表示类型。

在这个内部数据结构中,Console类型定义的方法每个方法都有一个对应的记录项。每一个记录项都有一个地址,根据这个地址可以找到方法的实现。在这个结构初始化时,每一个记录项被设置成指向CRL内部的一个函数JITCompiler(JIT编译器)

6. JIT编译IL为本地代码

CLR创建完成引用类型的内部数据结构后,JIT编译器完成Mian方法的编译,Main方法开始执行。Main方法首次调用WriteLine时,JITComplier函数会被调用(因为WriteLine指向JITComplier函数)。JIT编译器知道要调用的是哪个方法,以及具体是什么类型定义了该方法。然后,JITComplier会在该类型所在的程序集的元数据中查找被调用方法的IL代码所在,接着JITCompliers验证IL代码,并将IL代码编译成本机CPU指令。本机CPU指令被保存到动态分配的内存中。然后,JITComplier回到CLR为类型创建的内部数据结构,找到与被调用的方法对应的那条记录,修改最初对JITComplier的引用,使其指向内存块(包含刚才编译好的本机CPU指令)的地址,最后,从JITComplier函数跳转到内存块中的代码,这些代码正是WriteLine方法的实现。代码执行完毕后并返回时,会回到Main中的代码,并像平常一样继续执行。

当第二次调用WriteLIne时(WriteLine执行的是内存块),这一次,由于已经对WriteLine的代码进行了验证和编译,所以会直接执行内存块中的代码,完全跳过JITComplier函数。Write函数执行完毕后,会再次回到Main继续执行。

7. 程序的退出

JIT编译器将本机CPU指令存储到动态内存中。这意味着一旦应用程序终止,编译好的代码会被丢弃。所以,将来再次运行应用程序,或者同时启动应用程序的两个实例,JIT编译器必须再次将IL代码编译成本机指令。这可能显著增加内存耗用,但是,一般而言,JIT编译器造成的性能损失并不显著,因为大多数应用程序都反复调用相同的方法。程序运行期间。这些方法只会对性能造成一次性的影响。

时间: 2024-10-07 23:03:45

.net应用程序运行的背后机制的相关文章

C程序运行的背后(2)

话说上回说到,C程序运行之前,必须要加载到其进程地址空间中.今儿咱就扯扯这个加载到底是怎么加载的. 一图胜前言,这个图简单说明了可执行文件加载过程的逻辑流,在此只做粗粒度概要说明.需要准确描述的,请出门左转,看源码去吧. 1.  程序总是运行在进程上下文(context)中的,当输入./memlayout时,shell会创建一个子进程.除每个进程独有的专属信息外,子进程会继承父进程的大部分资源,如环境变量.进程空间映像等.也就是说,如果不重置子进程的内容,子进程会运行与父进程一样的程序.为了让子

C程序运行的背后(1)

一个成功的男人背后,至少有一个伟大的女人:一个不成功的男人,至少有一双手. 而一个C程序,无论成功不成功,它的背后一定有一个操作系统,一个shell,一套工具链. 世界本就不公平.隐藏在显而易见的事实背后的,你若能看透,便可以站在对自己公平的那一端. 1.进程地址空间 一个进程一旦建立,就会自认为占有4G内存(X86_32),这个内存被称作虚拟内存,也就是进程的地址空间.在Linux下,进程地址空间的布局大致如下图所示,其中的用户空间大致由这些部分组成: 代码段 初始化数据段 未初始化数据段 堆

Java程序运行机制及运行过程

Java运行机制 Java虚拟机(Java Virtual Machine):Java虚拟机可以理解成一个以字节码为机器指令的CPU:对于不同的运行平台,有不同的虚拟机:Java虚拟机机制屏蔽了底层运行平台的差别,真正实现了“一次编译,随处运行”. Java垃圾回收(Garbage Collection):不用使用的内存空间应该回收:在C/C++等语言中,由程序员负责回收无用的内存:Java语言消除了程序员回收无用内存的职 责,它提供一种系统级线程跟踪存贮空间的分配情况,并在JVM空闲的时候,检

java入门(1) 程序运行机制及运行过程

首先我们来看一下java程序在底层是怎么工作的: JAVA有两种核心机制: Java虚拟机(Java Virtual Machine): 1.java虚拟机可以理解成一个以字节码为机器指令的CPU. 2.对于不同的平台,有不同的虚拟机. 3.java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,随处运行”. 垃圾收集机制(Garbage collection) 1.不再使用的内存空间应回收——垃圾收集 2.Java语言相对于C/C++而言消除了程序员回收无用内存空间的责任:提供了一种系统

苹果IOS,与windows Phone7,系统,内存,CPU处理,及后台程序运行,详解微软墓碑机制的系统

关于ios的多任务以及内存管理 看了很多人为自己的可用内存是350mb还是380mb纠结.为了多优化出一点可用内存费脑筋. ios的任务管理和内存管理,跟windows是有很大差别的.很多人习惯于用 windows的思维去看待ios. windows大家都知道,窗口开的越多,系统越慢,为什么呢?因为所有窗口都在运行,cpu占用率高:并且都占内存.可用内存不足还会迫使系统使用硬盘充当虚拟内存,硬盘频繁读写当然会多耗电,并且硬盘速度也比较慢. ios则不同.首先ios的后台任务,除了极少数可以后台运

Python程序运行流程与垃圾回收机制

Python程序运行流程 Python解释器首先将程序将py文件编译成一个字节码对象PyCodeObject(只存在于内存中).(当这个模块的 Python 代码执行完后,就会将编译结果保存到了pyc文件中,这样下次就不用编译,直接加载到内存中.pyc文件只是PyCodeObject对象在硬盘上的表现形式.) py文件被编译后,接下来的工作就交由 Python虚拟机来执行字节码指令.Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令,并在当前的上下文环境中执行

Java 程序运行机制

目录 Java 程序运行机制 运行过程 注释 JDK, JRE, JVM 第一个 JAVA 程序 java命名规范 入门小游戏 Java 程序运行机制 运行过程 源文件 (a.java) Java 编译器 字节码文件(a.class) 进入 JRE ,分别执行 类装载器-->字节码校验器-->解释器 系统平台,执行. 注释 JRE 中包含 JVM (JAVA虚拟机); 其中的字节码校验器也是 JAVA 安全性的一种体现. 也正是因为有 JVM 的存在, 使得 JAVA 具有挂平台的特性; JV

图文浅析APK程序运行的过程

概述 APK程序运行过程有别于FrameWork底层启动过程,它们是倆码事,本文将以图文方式总结一下APK启动的过程,主要分为一下部分 [1]基本概念 [2]APK过程 1 .新的知识点 [1]什么是UI线程与Thread线程区别 UI线程并不陌生,但是这玩意到底是啥,与普通线程Thread有啥区别呢? 什么是UI线程: ActivityTread类所在的线程即为UI线程,负责用户交互,处理用户消息绘制界面等 区别: UI线程的ActivityTread中的Main方法已经使用Looper.pr

Java中Iterator(迭代器)的用法及其背后机制的探究

在Java中遍历List时会用到Java提供的Iterator,Iterator十分好用,原因是: 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构.迭代器通常被称为“轻量级”对象,因为创建它的代价小. Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个Iterator.第一次调用Iterator的next()方法时,它返回序列的第一个元素.注意:iterator()方法是jav