zoukankan      html  css  js  c++  java
  • 学习CLR Via C#的一些体会

    一个.net程序的创建过程

    1.创建helloworld.cs;
    using System;
    void main()
    {
        string str="helloworld";

     a a1=new a();

        Console.WriteLine(str);
    }

    class a

    {}

    2.csc \o:helloworld.exe helloworld.cs ,编译为一个exe文件,也就是一个托管模块,在这一步发生的事情很多,
    编译器将c#代码编译为IL代码,组成一个标准的PE文件,就是托管模块,
    这个托管模块包括PE32+头、CLR头、IL代码和元数据,
    PE32+头:标准的PE头,但是其中会包括一个清单(manifest)的数据块,描述了构成程序集的文件
    CLR头包括所要求的CLR版本,一些标志、入口方法(main)的methoddef元数据标志,以及模块的元数据、资源、强名称等数据项的位置和大小;
    IL代码:运行时由CLR编译成本地CPU指令
    元数据:两种类型的表:一种类型描述源代码中定义的类型和成员,另一种类型描述源代码中引用的类型和成员
    3,执行helloworld.exe文件
    首先,windows系统检查exe的PE头,判断是创建32位/64位进程,然后创建进程,分配进程空间
    然后,在进程空间中加载MSCorEE.dll,
    第三,进程的主线程执行MSCorEE.dll内部定义的一个方法,这个方法初始化CLR,加载程序集,然后调用其入口方法(main)。(程序集是一个抽象概念,在这里指的就是helloworld.exe这个托管模块,因为程序集中只有这一个模块,ps:还记得吗,模块中有PE32+头,CLR对程序集处理时,会全部忽略PE头,呵呵,PE头在经过windows系统检查以及创建进程后就被抛弃了)。
    第四,IL代码开始执行了。记住IL代码是由JIT编译器即时编译后运行的

    4,执行IL代码的过程:

    CLR查找元数据,确保所有所需的程序集,如MSCORLIB.DLL等程序集都加载到AppDomain中
    这个进程中只有一个线程,就是主线程,这个线程被创建时,会分配到1M的堆栈,这个堆栈用于存储方法的局部变量和实参,这里,string str="helloworld";str是局部变量,所以入栈,然后有Console类,这个类来自于system命名空间,那么这里JIT会在托管堆中创建Console的类型对象,其字段包括类型对象指针、同步块索引(有一位用于标示可达状态)和静态字段,然后调用类型对象的静态方法WriteLine。

    PS:

    *new一个对象,这里没有写new语句,在创建对象时,值类型放入堆栈中,引用类型放入托管堆,值类型不会发生垃圾回收,垃圾回收只发生在托管堆。
    *回收资源
    垃圾收集使用代的机制,0代对象是最近分配的对象,1代是0代回收后存活的对象,2代是1代回收后存活的对象
    *垃圾收集的时机:
    1,0代对象充满;
    2,调用GC.COLLECT()
    3,卸载AppDomain
    4,windows报告内存不足
    5,CLR关闭
    *垃圾收集的过程包括:
    1,标记阶段,遍历线程堆栈检查所有根,并遍历所有可达对象
    2,压缩阶段,压缩时所有线程必须挂起,垃圾收集器遍历堆并搬迁非垃圾对象到哪些连续的区块以压缩托管堆,然后,垃圾收集器还必须遍历所有根,修改他们使其指向这些对象的新的位置

    *什么是终结操作(Finalize)

    ~aa();就是对象的终结操作,用于释放非托管资源。

    *对于终结操作的特殊处理:
    1,当new一个对象时,new操作符在托管堆上分配空间,如果类型定义了Finalize方法,那么在该类型的实例构造器调用之前,将一个指向这个对象的指针放入“终结列表”,当垃圾收集时,如果这个对象不可达,那么将垃圾收集器不会马上回收这个对象的空间,而是将终结列表上的指针放入“终结可达列表”,执行回收时,垃圾收集器将托管堆上的其它对象回收,这个对象不会回收。一个特殊的线程会清空“终结可达列表”,同时执行这个对象的Finalize方法。下一次垃圾收集时,这个对象因为不可达,垃圾收集器回收托管堆上的这个对象的空间。实际上,因为对象的代会提升,所以释放一个对象占用的内存可能的收集次数会超过两次。

    CLR怎样调用静态方法:定位在堆中的类型对象,然后JIT编译这个类型对象的静态方法,然后执行;
    怎样调用非虚实例方法:根据变量的类型找到在堆中的类型对象,查看是否有该方法,没有则向上回溯查找,然后编译实例方法,执行
    怎样调用虚实例方法:根据变量的地址找到发出调用的对象,然后检查这个对象的内部类型指针成员,这个成员引用了对象的实际类型,然后编译实际类型的方法,执行
    所有的类型对象都指向一个Type类型对象,而这个Type类型对象则指向本身。呵呵,具体看书

  • 相关阅读:
    统计学习方法学习笔记第二章(感知机)
    filebeat句柄占用问题
    小组年终总结的汇总
    使用docker制作zookeeper镜像
    普罗米修斯在k8s上面的部署
    k8s亲和性和反亲和性的理解
    AlertManager 的在k8s集群上面的安装部署使用
    filebeat生产上面镜像制作的流程
    普罗米修斯生产上面的性能优化点
    AlertManger集群的搭建
  • 原文地址:https://www.cnblogs.com/malingbo/p/2398142.html
Copyright © 2011-2022 走看看