C#之托管与非托管

  • 资源:在本地化中,资源用于翻译文本和图像。在这里,资源用于另外一个主题:使用托管和非托管的资源——存储在托管或本机堆中的对象。

后台内存管理

  • 值数据类型
    • 在任何复杂的高级语言中,编译器负责把人们能够理解的变量名转化为处理器能够理解的内存地址
    • 在处理器的虚拟内存中,有一个区域成为栈。栈存储不是成员对象的值数据类型
    • 首先声明变量a,接着在内部代码块声明了b。而后内部代码块终止,b就超出做用域,最后a超出做用域。
    • 程序第一次开始运行时,栈指针指向为栈保留的内存块末尾。栈实际上时向下填充的,即从高内存地址到低内存地址填充。当数据入栈时,栈指针就会随之调整,以始终指向下一个空闲存储单元。
  • 引用数据类型php

    • 咱们但愿使用一个方法分配内存,来存储一些数据,并在该方法退出后的很长一段时间内数据还是可用的。(例如使用new运算符来请求分配存储空间)。对于全部引用类型,此时都使用托管堆
    • 托管堆(简称堆):处理器可用内存中的另外一个存储区域。
      void DoWork()
      {
          Customer arabel;
          arabel=new Customer();
          Customer otherCustomer2=new EnhanceCustomer();
      }
    • Customer arabel 声明一个Customer引用arabel,在栈上给一个引用分配存储空间,但这仅仅是一个引用,而不是实际的Customer对象
    • arabel=new Customer() 分配堆上的内存,一存储Customer对象(一个真正的对象,不仅是地址)。而后把arabel的值设置为分配给新Customer对象的内存地址
    • 总结:new方法其实就是栈+堆来储存的。栈上记录引用,堆上记录对象自己。
    • 注意:与栈不一样,堆的内存是向上分配的,因此空闲空间在已用空间的上方
  • 垃圾回收
    web

    • 垃圾回收器运行时,他会从堆中删除再也不引用的全部对象。
    • 垃圾回收器释放了能释放的全部变量,就会把其余对象移动会堆的顶部,再次造成连续的内存块

    注意:能够调用System.GC.Collect()方法,强迫垃圾回收器在代码的某一个地方运行。数据库

  • 建立对象时,会把这些对象放在托管堆上。堆的第一部分红为第0代。建立新对象时,会把他们移动到堆的这个部分中,所以,这里驻留了最新的对象。
  • 对象会继续放在这一部分,知道垃圾回收过程第一次进行回收。这个清理过程以后仍保留的对象会被压缩,而后移动到堆的下一部分上或世代部分——第一代对应的部分。
  • 此时第0代对应的部分为空,全部新的对象都再次放在这一部分。
    在给对象分配内存空间时,若是超出了第0代对应的部分的容量,或者调用了CG.Collect()方法,就会进行垃圾回收。
  • 在.NET中,垃圾回收提升性能的另外一个领域时架构处理堆上较大对象的方式。在.NET下,较大对象有本身的托管堆,称为大托管堆(大于85000个字节的对象)。
  • 为了利用包含大量内存的硬件,垃圾回收过程增长了GCSettings.LatencyMode属性
成员 说明
Batch 禁用并发设置,把垃圾回收设置为最大吞吐量。这会重写配置设置
Interactive 工做站的默认设置。它使用垃圾回收并发设置,平衡吞吐量和响应
LowLatency 保守的垃圾回收。只有在系统存在内存压力时,才能进行完整的回收。只应用于较短期,执行特定的操做。
SustainedLowLatency 只有在系统存在内存压力时,才能进行完整的内存块回收
NoGCRegion 成功调用TryStartGCRegion后,指定不该运行的垃圾回收器,直到调用EndNoRegion为止。
  • 强引用和弱引用
    安全

    • 垃圾回收器不能回收仍在引用的对象的内存——这是一个强引用。它能够回收不在根表中直接或间接引用的托管内存。

    若是对象相互引用,但没有在根表中引用,例如:对象A引用B,B引用C,C引用A,则GC能够销毁这些全部的对象网络

  • 弱引用:
    var myWeakPeference =new WeakReference(new DataObject());
    if(myWeakPeference.IsAlive)
    {
        DataObject strongReference =myWeakPeference.Target as DataObject;
        if(strongReference!=NULL)
        {
            //使用强引用
        }
        else
        {
            //不可音乐
        }
    }
  • 处理非托管的资源架构

    • 释放非托管资源(文件句柄、网络链接、数据库链接等)
    • 在定义一个类时,可使用两种机制来自动释放非托管资源
    • 声明一个析构函数(或终结器),做为类的一个成员
    • 在类中实现System.IDisposable接口
    • 析构函数或终结器
    • C#在编译析构函数时,会隐式地把析构函数的代码编译为等价于重写Finalize()方法的代码,从而确保执行父类的Finalize()方法。下面等价于编译器为~MyClass()生成的IL:

      protected override void Finalize()
      {
      try
      {
      //FInalizer implements
      }
      finally
      {
      base.Finalize();
      }
      }
    • IDisposable接口(推荐)
    • Dispose() :显示地释放由对象直接使用地全部非托管资源。

      ResourceGobbler theInstance=null;
      try
      {
      theInstance = new ResourceGobbler();
      }
      finally
      {
      theInstance.Dispose;
      }
    • using语句
    • 下面的代码生成与try等价的IL代码

      using (var theInstance = new ResourceGobbler())
      {
      //do your processing
      }

      using 语句的后面是一对圆括号,其中是引用变量的声明和实例化,该语句使变量的做用域限定在随后的语句块中。另外,在变量超出做用域时,即便出现异常,也会自动调用其Dispose()方法并发

    • 实现IDisposable接口和析构函数

    前面讨论到自定义类所使用的释放非托管资源的两种方式:
    • 1.利用运行库强制执行的析构函数,但析构函数的执行时不肯定的,并且因为垃圾回收器的工做方式,它会给运行库增长不可接受的系统开销
    • 2.IDisposable接口提供了一种机制,该机制容许类的用户控制释放资源的时间,但须要确保调用Dispose()方法
    • 若是建立了终结器,就应该实现IDisposable接口。下面是一个双重实现的例子(正确调用Dispose(),同时把实现析构函数做为一种安全机制)ide

      using System;
      public class ResourceHolder : IDisposable
      {
          private bool _isDisposed=false;
          public void Dispose()
          {
              Dispose(true);
              GC.SuppressFinalize(this);
          }
      
          protected virtual void Dispose(bool disposing)
          {
              if(!_isDisposed)
              {
                  if(disposing)
                  {
                      //Cleanup managed objects by calling their
                      //Dispose() Methods
                  }
                  Cleanup unmanaged objects
              }
              _isDisposed=true;
          }
      
          ~ResourceHolder()
          {
              Dispose(false);
          }
      
          public void someMethod()
          {
              //Ensure object not already disposed before excution of any method
              if(_isDisposed)
              {
                  throw new ObjectDisposedException("ResourceHolder");
              }
              //method implementation
          }
      }
      • 解析:
        • 1.Dispose()方法有第二个protected重载方法,它带一个布尔参数,这是真正在完成清理工做的方法。Dispode(bool)方法由析构函数和IDispose.Dispose()方法调用。
  • 不安全的代码

    • 用指针直接访问内存svg

      • 使用指针的主要缘由:向后兼容+性能
      • unsafe关键字编写不安全的代码:(C#只容许在特别标记的代码块中使用指针)
        unsafe int GetSomeNumber()
        {
            //code that can use pointers
        }

      unsafe :能够标记在类、成员、方法中函数

    • 指针的语法(略)

    • 将指针强制转化为整数类型
    • 指针类型之间的强制转换
    • void指针:若是要维护一个指针,但不但愿指定它指向的数据类型,就能够把指针声明为void

      int *pointerToInt;
      void *pointerToVoid;
      pointerToVoid=(void)pointerToInt;
    • 指针的算术运算:int+1表明地址+4。(智能)
    • sizeof运算符:
    • 结构指针:指针成员访问运算符

      • 结构指针的工做方式与预约义值类型的指针的工做方式相同。但有一个条件:结构不能包含任何引用类型(由于指针不能指向任何引用类型)
        struct MyStruct
        {
            public long X;
            public float F;
        }
        MyStruct *PMystruct;
        var mystruct =new MyStruct();
        PMystruct=&mystruct;
        
        (*PMystruct).X;
        PMystruct->Y;
    • 类成员指针

      • 前面说过,不能建立指向类的成员指针,这是由于垃圾回收器不维护关于指针的任何信息,只维护关于引用的信息。可是,绝大多数类都包含值类型的成员,能够为这些值类型的成员建立指针,但这须要一种特殊的语法:使用fixed关键字
        class MyClass
        {
            public long X;
            public float F;
        }
        
        var myObject = new MyClass();
        fixed(long* pX =&(myObject.X))
        fixed(float* pF=&(myObject.F))
        {
            //do something
        }
    • 使用指针优化性能