一、托管堆基础

1,访问一个资源(文件、内存缓冲区、屏幕空间、网络连接、数据库资源等)所需的步骤

①调用IL指令newobj,为代表资源的类型分配内存(一般使用c# new操作符来完成)

②初始化内存,设置资源的初始状态并使资源可用。类型的实例构造器负责设置初始状态

③访问类型的成员来使用资源(有必要可以重复)

④摧毁资源的状态以进行清理

⑤释放内存。垃圾回收器独自负责这一步

 

2,从托管堆分配资源

初始化进程时,CLR划出一个地址空间区域作为托管堆,一个区域被非垃圾对象填满后,CLR会分配更多的区域(32位进程最多能分配1.5GB,64为进程最多能分配8TB)。CLR还要维护一个指针(NextObjPtr),该指针指向下一个对象在堆中的分配位值。刚开始的时候,NextObjPtr设为地址空间区域的基地址。

 

3,C#的new操作符导致CLR执行以下步骤

①计算类型的字段(以及从基类型继承的字段)所需的字节数。

②加上对象的开销所需的字节数。每个对象都有两个开销字段:类型对象指针和同步快索引(32位:两个字段各需32位,所以每个对象要增加8字节。64位:每个字段各需64位,所以每个对象要增加16字节)(int=4字节;long=8字节)

③CLR检查区域中是否有分配对象所需的字节数。如果托管堆有足够的可用空间,就在NextObjPtr指针指向的地址处放入对象,为对象分配的字节会被清零。接着调用类型的构造器(为this参数传递NextObjPtr),new操作符返回对象的引用。就在返回这个引用之前,NextObjPtr指针的值会加上对象占用的字节数来得到一个新值,即下一个对象放入托管堆是的地址

 

4,垃圾回收算法

CLR使用一种引用跟踪算法。引用跟踪算法只关心引用类型的变量,因为只有这种变量才能引用堆上的对象,我们将所有引用类型的变量都称为

①CLR开始GC时,首先暂停进程中的所有线程(这样可以防止线程在CLR检查期间访问对象并更改其状态)

②CLR进入GC标记阶段(这个阶段,CLR遍历堆中所有对象,将同步块索引字段中的一位设为0。这表明所有的对象都应该删除)

③CLR检查所有活动根(根为null,则CLR忽略这个根),查看他们引用了那些对象。如果引用了堆上的对象,CLR都会标记那个对象(将对象的同步块索引中的位设置为1)

④检查完毕后,堆中的对象要么标记。要么未标记。已标记的对象不能被垃圾回收,因为至少有一个根在引用它,我们说这些对象时可达

⑤进入GC的压缩阶段,在这个阶段,CLR对堆中已标记的对象进行“乾坤大挪移”,压缩所有幸存下来的对象,使它们占用连续的内存对象

⑥压缩之后,根现在的引用还是原来的位置,而非移动之后的位置。所以作为压缩阶段的一部分,CLR还要从每个根减去所引用的对象在内存中的偏移的字节数。这样就能保证根还是引用和之前一样的对象;只是对象在内存中换了位置

 托管堆和垃圾回收

 

5,垃圾回收和调试

 

①使用Release编译后,允许可执行文件,会发现TimerCallback方法只被调用了一次。因为Timer在初始化之后再也没有用过变量t。(调试模式下Timer对象不会被回收)

        static void Main(string[] args)
        {
            //创建没2000毫秒就调用一次TimerCallback方法的timer对象
            Timer t = new Timer(TimerCallback, null, 0, 2000);
            Console.ReadLine();
        }
        private static void TimerCallback(object o)
        {
            Console.WriteLine("a");

            //出于演示目的,强制执行一次垃圾回收
            GC.Collect();
        }

②显示要求释放计时器,它才能活到被释放的那一刻

        static void Main(string[] args)
        {
            //创建没2000毫秒就调用一次TimerCallback方法的timer对象
            Timer t = new Timer(TimerCallback, null, 0, 2000);
            Console.ReadLine();
            //在ReadLine之后引用t(在Dispose方法返回之前,t会在GC中存活)
            t.Dispose();
        }
        private static void TimerCallback(object o)
        {
            Console.WriteLine("a");

            //出于演示目的,强制执行一次垃圾回收
            GC.Collect();
        }

 

二、代:提升性能

对象越新,生存期越短
对象越老,生存期越长
回收堆的一部分,速度快于回收整个堆
1,原理
①CLR初始化堆时为0代和1代选择预算容量(以kb为单位)。后期CLR会自动调节预算容量
②如果分配一个新的对象造成第0代超过预算,就必须启动一次垃圾回收
③经过垃圾回收之后,第0代的幸存者被提升到1代(第一代的大小增加);第0代又空了出来
④由于第0代已满,所以必须垃圾回收。但这一次垃圾回收器发现第1代用完了预算容量。所以这次垃圾回收器决定检查第1代和第0代的所有对象。两代被垃圾回收以后,第1代的幸存者提升到2代,第0代的幸存者提升到1代

2,垃圾回收触发的条件
①最常见触发条件:CLR在检查第0代超过预算时触发一次GC
②代码显示调用Sytem.GC的静态Collect方法
③Windows报告底内存情况
④CLR正在卸载AppDomain
⑤CLR正在关闭(CLR在进程正常终止时)

3,大对象
目前认为85000字节或更大的对象时大对象。(之前讨论的都是小对象)。大对象一般是大字符串(比如XML或JSON)或者用于I/O操作的字节数组(比如从文件或网络将字节读入缓冲区一遍处理)
①大对象不是在小对象的地址空间分配,而是在进程地址空间的其他地方分配
②目前版本的GC不压缩大对象,因为在内存中移动它们的代价过高
③大对象总是第2代,绝不可能是第0代或者第1代

4,垃圾回收模式

CLR启动时会选择一个GC模式,进程终止前该模式不会变。

①两个主要模式:

1>工作站

该模式针对客户端应用程序优化GC。GC造成的延时很低,应用程序线程挂起时间很短,避免是用户感到焦虑。

2>服务器

该模式针对服务器应用程序优化GC。被优化的主要是吞吐量和资源利用。

②应用程序模式以“工作站”GC模式运行

③显示告诉CLR使用服务器回收站

  <runtime>
    <gcServer enabled="true"></gcServer>
  </runtime>
            //询问CLR它是否在“服务器”GC模式中运行
            Console.WriteLine(GCSettings.IsServerGC);
            Console.ReadLine();

 ④两个子模式(并发(默认)或非并发)

在并发模式中,垃圾回收器有一个额外的后台线性,它能在应用程序运行时并发标记对象

  <runtime>
    <!--告诉CLR不要使用并发回收器-->
    <gcConcurrent enabled="false"></gcConcurrent>
  </runtime>

 ⑤GCSettings的LatencyMode属性对垃圾回收进行某种程度的控制

符号名称

说明

Batch(“服务器”GC模式的默认值)

关闭并发GC

Interactive(“工作站”GC模式的默认值)

打开并发GC

LowLatency

在短期的、时间敏感的操作中(如果动画绘制)使用这个延迟模式。这些操作不适合对第二代进行回收

Sustained LowLatency

使用这个延迟模式,应用程序的大多数操作都不会发生长的GC暂停。只要有足够的内存,它将禁止所有会造成阻塞的第二代回收操作。事实上,这种应用程序(例如需要迅速响应的股票软件)的用户应该考虑安装更多的RAM来防止发生生长的GC暂停

⑥正确的使用LowLatency

        static void Main(string[] args)
        {

            GCLatencyMode oldModel = GCSettings.LatencyMode;
            Console.WriteLine(oldModel);
            
            //约束执行区域(CER)
            System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                GCSettings.LatencyMode = GCLatencyMode.LowLatency;
                //在这里运行你的代码...
            }
            finally
            {
                GCSettings.LatencyMode = oldModel;
            }
            Console.ReadLine();

        }
View Code

相关文章:

  • 2021-05-30
  • 2022-12-23
  • 2022-12-23
  • 2021-09-08
  • 2021-11-12
  • 2022-12-23
  • 2021-11-09
  • 2022-02-20
猜你喜欢
  • 2021-11-17
  • 2021-06-21
  • 2022-12-23
  • 2021-07-13
  • 2021-10-19
  • 2022-01-19
相关资源
相似解决方案