zoukankan      html  css  js  c++  java
  • cir from c# 托管堆和垃圾回收

    1,托管堆基础

      1. 调用IL的newobj 为资源分配内存
      2. 初始化内存,设置其初始状态并使资源可用.类型的实列构造器负责设置初始化状态
      3. 访问类型的成员来使用资源
      4. 摧毁状态进行清理
      5. 释放内存//垃圾回收期负责.

    2,从托管堆创造对象

    1. 进程初始化时候,CLR划出一个地址空间区域作为托管堆,并且初始化NextObjPtr指针,其指向下一个可用的托管堆地址.
    2. c#new操作 首先计算类型的字段所需要的字节数
    3. 加上对象开销的字节数:类型对象指针和同步块索引.64位机器上是16字节,32位机器是8字节.
    4. CLR检查是否空间够,如果够,则在指向地址处放入对象,并且将对象指针和同步块索引清0.接着,调用构造器.
    5. image

    3,垃圾回收算法

    我们先来看垃圾回收的算法与主要流程:
    算法:引用跟踪算法。因为只有引用类型的变量才能引用堆上的对象,所以该算法只关心引用类型的变量,我们将所有引用类型的变量称为
    主要流程:
    1.首先,CLR暂停进程中的所有线程。防止线程在CLR检查期间访问对象并更改其状态。
    2.然后,CLR进入GC的标记阶段。
     a. CLR遍历堆中的对象(实际上是某些代的对象,这里可以先认为是所有对象),将同步块索引字段中的一位设为0,表示对象是不可达的,要被删除。
     b. CLR遍历所有,将所引用对象的同步块索引位设为1,表示对象是可达的,要保留。
    3.接着,CLR进入GC的碎片整理阶段。
     a. 将可达对象压缩到连续的内存空间(大对象堆的对象不会被压缩)
     b. 重新计算所引用对象的地址。
    4.最后,NextObjPtr指针指向最后一个可达对象之后的位置,恢复应用程序的所有线程。

    image

    重要提示:静态字段引用对象会一直存在.内存泄漏的常见原因是静态字段引用某个集合对象,然后不停添加数据项.因此,尽量避免使用静态变量.

    4,垃圾回收和调试

     public class GCRef
        {
            private static void TimerCallback(Object o)
            {
                Console.WriteLine("In TimerCallBack:" + DateTime.Now);
                GC.Collect();
            }
            public static void Go()
            {
                Timer t = new Timer(TimerCallback, null, 0, 2000);
                Console.ReadKey();
                t.ToString();//使用该对象也可以使对象存活
                t.Dispose();//使用显式垃圾回收的方式,进行垃圾回收.
            }
        }

    由于进行垃圾回收,所以,只会回调一次该方法.当后面再使用方法的时候,才能够一致保存对象t.

    5,垃圾回收的代的概念

    • 对象越新,生存期越短
    • 对象越老,生存期越长
    • 回收堆的一部分,速度快于回收整个堆
    • GC一个有3代0代,1代,2代

    6,代的生成机制

    • 初始化后,所有添加到堆中的对象都是0代.当0代对象操作某个预算容量的时候,进行垃圾回收.回收后剩下的,就是1代对象
    • 重复上面的过程,然后1代对象不断的增加,知道其值超过某个预算容量,然后进行1代和0代的垃圾回收.1代剩余的到2代中;
    • 1代剩余的对象到2代中,0代剩余的到1代中.0代清空.

    7,GC.ReRegisterForFinalize(Object) 方法

     public static class GCNotification
        {
            private static Action<int> s_gcDone = null;
    
            public static event Action<int> GCDone
            {
                add
                {
                    if (s_gcDone == null) { new GenObject(0);new GenObject(2); }
                    s_gcDone += value;
                }
                remove
                {
                    s_gcDone -= value;
                }
            }
            private sealed class GenObject
            {
                private int m_generation;
                public GenObject(int generation) { m_generation = generation; }
                ~GenObject()
                {
                    if (GC.GetGeneration(this) >= m_generation)
                    {
                        Action<int> temp = Volatile.Read(ref s_gcDone);
                        if (temp != null) temp(m_generation);
                    }
    
                    if ((s_gcDone != null) && (!AppDomain.CurrentDomain.IsFinalizingForUnload()) && (!Environment.HasShutdownStarted))
                    {
                        if (m_generation == 0) new GenObject(0);
                        else GC.ReRegisterForFinalize(this);
                    }
                    else { } //让对象被回收}
                }
    
            }
            public static void Go()
            {
                GCDone += x => Console.WriteLine($"GenObject {x}");
                GC.Collect();
    
            }

    思路,1,自定义了事件,并且世界的通知对象类型是Action<int>,并且手动进行了触发.在垃圾回收的代>设定代m_generation时.

          

    8,Finalization Queue和Freachable Queue-ReRegisterFinalize()和SupressFinalize()函数

    一个将对象指针从Finalization队列中去除,一个重新加入到Finalization队列中.

    这两个队列和.net对象所提供的Finalize方法有关。这两个队列并不用于存储真正的对象,而是存储一组指向对象的指针。当程序中使用了new操作符在Managed Heap上分配空间时,GC会对其进行分析,如果该对象含有Finalize方法则在Finalization Queue中添加一个指向该对象的指针。在GC被启动以后,经过Mark阶段分辨出哪些是垃圾。再在垃圾中搜索,如果发现垃圾中有被Finalization Queue中的指针所指向的对象,则将这个对象从垃圾中分离出来,并将指向它的指针移动到Freachable Queue中。这个过程被称为是对象的复生(Resurrection),本来死去的对象就这样被救活了。为什么要救活它呢?因为这个对象的Finalize方法还没有被执行,所以不能让它死去。Freachable Queue平时不做什么事,但是一旦里面被添加了指针之后,它就会去触发所指对象的Finalize方法执行,之后将这个指针从队列中剔除,这是对象就可以安静的死去了。.net framework的System.GC类提供了控制Finalize的两个方法,ReRegisterForFinalize和SuppressFinalize。前者是请求系统完成对象的Finalize方法,后者是请求系统不要完成对象的Finalize方法。ReRegisterForFinalize方法其实就是将指向对象的指针重新添加到Finalization Queue中。这就出现了一个很有趣的现象,因为在Finalization Queue中的对象可以复生,如果在对象的Finalize方法中调用ReRegisterForFinalize方法,这样就形成了一个在堆上永远不会死去的对象,像凤凰涅槃一样每次死的时候都可以复生。

    public class MyFinalizeObject1//新建一个带终结器的会死灰复燃的类
        {
            public static int CountFinalized = 0;//类终结次数
            public string Name { get; }//类名
            public MyFinalizeObject1(string name)//类构造器
            { Name = name; }
            public static void Go()
            {
                new MyFinalizeObject1("abc");
                Timer t = new Timer(x => GC.Collect(), null, 0, 2000);
                Console.ReadKey();
                t.Dispose();
            }
            ~MyFinalizeObject1()//将在Finalization Queue队列中的类首先放入 Freachable Queue中,然后调用终结器,然后再释放.
            {
                Console.WriteLine(this.GetType().ToString() + $" {this.Name} has been DC {CountFinalized++}");//类每次终结输出信息
                GC.ReRegisterForFinalize(this);//将类重新放入终结队列. 
            }
        }

    在GO中创建一个Threading.TImer对象,让其每2秒实行一次垃圾显示回收

    ClrFromCSharp_2_2.LearnGC.MyFinalizeObject1 abc has been DC 0
    ClrFromCSharp_2_2.LearnGC.MyFinalizeObject1 abc has been DC 1
    ClrFromCSharp_2_2.LearnGC.MyFinalizeObject1 abc has been DC 2
    

    9,垃圾回收模式:

    • 工作站:针对客户端优化,GC造成延迟很低
    • 服务器:托管堆被分配到每个CPU一个GC,每个GC负责各自区域,并且在每个CPU上面运行特殊的线程:并发回收垃圾.
    • 配置文件告诉使用服务器回收器
      • <?xml version="1.0" encoding="utf-8"?>
        <configuration>
          <runtime>
            <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
              <probing privatePath="AuxFiles"/>//在哪个位置寻找程序集.
            </assemblyBinding>
            <gcServer enabled="true"/>
          </runtime>
            <startup>
                <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
            </startup>
        </configuration>
        
        
    •    使用GC.IsServerGC 来查询是否是这个模式.结果是True---上面配置了.
      public class MyFinalizeObject1//新建一个带终结器的会死灰复燃的类
          {
              public static int CountFinalized = 0;//类终结次数
              public string Name { get; }//类名
              public MyFinalizeObject1(string name)//类构造器
              { Name = name; }
              public static void Go()
              {
                  new MyFinalizeObject1("abc");
                  Console.WriteLine($"{GCSettings.IsServerGC}");//查看是否启用了服务器垃圾回收
                  Timer t = new Timer(x => GC.Collect(), null, 0, 2000);
                  Console.ReadKey();
                  t.Dispose();
              }
              ~MyFinalizeObject1()//将在Finalization Queue队列中的类首先放入 Freachable Queue中,然后调用终结器,然后再释放.
              {
                  Console.WriteLine(this.GetType().ToString() + $" {this.Name} has been DC {CountFinalized++}");//类每次终结输出信息
                  GC.ReRegisterForFinalize(this);//将类重新放入终结队列. 
              }
          }

            子模式:

    • 并发:占用更多资源,创建了额外的后台线程
    • 非并发----可以设定配置
      <?xml version="1.0" encoding="utf-8"?>
      <configuration>
        <runtime>
          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <probing privatePath="AuxFiles"/>
          </assemblyBinding>
          <gcServer enabled="true"/>//设定服务器模式
          <gcConcurrent enabled="false"/>//设定非并发模式
        </runtime>
          <startup>
              <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
          </startup>
      </configuration>

       自定义在低回收延迟模式进行绘画,动画等低延迟操作

    private static void LowLatencyDemo()
            {
                GCLatencyMode OldMode = GCSettings.LatencyMode;
                System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
                try
                {
                    GCSettings.LatencyMode = GCLatencyMode.LowLatency;
                    //自己的代码,低GC回收延迟
                }
                finally
                {
                    GCSettings.LatencyMode = OldMode;
                }
            }

    10-,强制垃圾回收GC.Collect(int Generation,GCCollectionMode mode,bool blocking)

    11,监视应用程序内存:

         11.1 在内存中使用函数

    ~MyFinalizeObject1()//将在Finalization Queue队列中的类首先放入 Freachable Queue中,然后调用终结器,然后再释放.
            {
                Console.WriteLine(this.GetType().ToString() + $" {this.Name} has been DC {CountFinalized++}");//类每次终结输出信息
                Console.WriteLine($"CollectionCount:{GC.CollectionCount(0)}");
                Console.WriteLine($"GetTotalMemery:{GC.GetTotalMemory(false)}");
                GC.ReRegisterForFinalize(this);//将类重新放入终结队列. 
            }

          11.2打开性能监视器---PerfMon.exe

       然后查看+,添加.net clr memeroy,勾选显示描述.

    image

    12.使用需要特殊清理的类型.任何包含本机资源的类型都支持终结---Finalize

    文件,网络连接,套接字,互诉体---都支持.


    ------------创建本机的托管资源类型时,强烈建议应该从System.Runtime.InteropServices.SafeHandle这个基类来进行派生.

    [System.Security.SecurityCritical]
    public abstract class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable

    该类的实现:

    protected SafeHandle (IntPtr invalidHandleValue, bool ownsHandle);
    参数
    invalidHandleValue
    IntPtr
    无效句柄的值(通常为 0 或 -1)。 IsInvalid 的实现应对此值返回 true。
    ownsHandle
    Boolean
    在终止阶段使 true 可靠地释放句柄,则为 SafeHandle;否则为 false(不建议使用)。
    注解
    如果 ownsHandle 参数 false,则永远不会调用 ReleaseHandle;因此,不建议使用此参数值,因为代码可能会泄漏资源。
    //也就是说,invalidHandleValue,只是无效的Handle的值.此时,IsInvalid返回true;
    另一个参数建议true,否则无法使用RelseaseHandle()函数,该函数用于释放资源.
     public abstract class SafeHandle : CriticalFinalizerObject, IDisposable
        {//只是本机资源句柄.
            protected IntPtr handle;
            //第二个参数指明,派生的对象被回收时,本机资源也被关闭.true---调用ReleaseHandle()函数.
            protected SafeHandle(IntPtr invalidHandlerValue, bool ownsHandle)
            {
                this.handle = invalidHandlerValue;
            }//第一个参数指明非实句柄的值0或-1,第二个指明,是否自定义释放句柄函数工作.
            protected void SetHandle(IntPtr handle)
            {
                this.handle = handle;
            }
            public void Dispose() { Dispose(true); }
            protected virtual void Dispose(Boolean disposing)
            {
                //默认实现忽略disposing参数
                //如果资源已经释放,那么返回.
                //如果ownsHandle为false,那么返回.
                //设置 标志位 来只是资源已经释放
                //调用虚方法 ReleaseHandle();
                //调用GC.SuppressFinalize(this)阻止调用Finalize方法
                //如果ReleaseHandle返回true,那么返回.
                //如果走到这一步,激活releaseHandleFaild托管调试助手(MDA)
            }
            ~SafeHandle() { Dispose(false); }
            protected abstract bool ReleaseHandle();//必须重写这个方法以实现释放资源代码
    
            public void SetHandleAsInvalid()
            {
                //调用GC.SuppressFinalize(this)方法来阻止调用Finalize方法.
            }
            public bool IsClosed
            {
                get { return true; } //返回指出资源是否释放的标志---Dispose(true)中设置.
            }
            public abstract bool IsInvalid//重写该属性,只是句柄值不代表资源,0或-1.
            {
                get;
            }
        }

    safeHandle派生的类有:

    • SafeHandle 是操作系统句柄的抽象包装器类。 从此类派生比较困难。 但可以使用 Microsoft.Win32.SafeHandles 命名空间中可提供以下项的安全句柄的派生类。
    • 文件(SafeFileHandle 类)。
    • 内存映射文件(SafeMemoryMappedFileHandle 类)。
    • 管道(SafePipeHandle 类)。
    • 内存视图(SafeMemoryMappedViewHandle 类)。
    • 加密构造(SafeNCryptHandle、SafeNCryptKeyHandle、SafeNCryptProviderHandle和 SafeNCryptSecretHandle 类)。
    • 进程(SafeProcessHandle 类)。
    • 注册表项(SafeRegistryHandle 类)。
    • 等待句柄(SafeWaitHandle 类)。

    以下是SafeHandleZeroOrMinusOneIsInvalid

    //从 SafeHandle 派生的类---SafeHandleZeroOrMinusOneIsInvalid:SafeHandle
    
    public abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle
        {
            protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) { }//默认设定句柄为0.
            public override bool IsInvalid//只是句柄为0,或者-1时候,则为Invalid形式.
            {
                 get
                 {
                     if (base.handle == IntPtr.Zero) return true;
                     if (base.handle == (IntPtr)(-1)) return true;
                     return false;
                 }
             }
         }
    

       以下是SafeFileHandle类

     public sealed class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            public SafeFileHandle(IntPtr preExistingHandle,bool ownsHandle) : base(ownsHandle) { base.SetHandle(preExistingHandle); }
            protected override bool ReleaseHandle()
            {
                return Win32Native.CloseHandle(base.handle);
            }
        }

    13,使用包装了本机资源的类型

    假定写代码创建一个临时文件,向其中写入一些字节,然后删除文件.会报出IOException错误,因为,在文件资源生存期无法删除此文件.

    public class CallSafeHandle
        {
            public static void Demo1()
            {
                byte[] bytes = { 1, 2, 3, 4, 5 };
                FileStream fs = new FileStream("Temp.dat", FileMode.Create);
                fs.Write(bytes, 0, bytes.Length);
                File.Delete("Temp.dat");
    
            }
        }

    将代码更改为如下----添加了,dispose();就可以正常运行了.

    byte[] bytes = { 1, 2, 3, 4, 5 };
                FileStream fs = new FileStream("Temp.dat", FileMode.Create);
                fs.Write(bytes, 0, bytes.Length);
                fs.Dispose();
                File.Delete("Temp.dat");

    注意:Dispose()函数只是释放资源,但是对象还是存在的.

    public static void Demo1()
            {
                byte[] bytes = { 1, 2, 3, 4, 5 };
                FileStream fs = new FileStream("Temp.dat", FileMode.Create);
                try
                {
                    fs.Write(bytes, 0, bytes.Length);
                }
                finally
                {
                    if (fs != null) fs.Dispose();
                }
    
                File.Delete("Temp.dat");
    
            }

    可以ji进一步更改为如下的形式  使用using.

    public static void Demo1()
            {
                byte[] bytes = { 1, 2, 3, 4, 5 };
    
    
    
                using(FileStream fs = new FileStream("Temp.dat", FileMode.Create))//相当于 Try...Finally结构,必须实现IDispose接口
                {
                    fs.Write(bytes, 0, bytes.Length);
                }
    
                File.Delete("Temp.dat");
            }

    利用 StreamWriter 进行文件读写操作.

    public static void Demo2()
            {
                using(StreamWriter sw=new StreamWriter(new FileStream("DataFile.txt", FileMode.OpenOrCreate)))
                {
                    sw.Write("Hi there");
                }
    
            }
        }

    注意:StreamWrite只会把数据写入到缓冲区里面,所以需要Dispose()函数才能真正写入进去.或者调用Flush().


    14,GC的其他功能----使用这个来防止以下的情况:

    1,托管类很小,资源类很大,比如位图,这样会造成内存用量很大.使用这个,可以提示GC提前,并且更频繁的进行垃圾回收.

    • GC.AddMemoryPressure(int64 byteallocated)
    • GC.RemoveMemoryPressure(Int64 byteallocated)
      private sealed class BigNativeResource
              {
                  private int m_size;
                  public BigNativeResource(int size)
                  {
                      if (m_size > 0) GC.AddMemoryPressure(m_size);//每个类提示实际消耗内存.
                      Console.WriteLine("BigNativeResource create.");
                  }
                  ~BigNativeResource()
                  {
                      if (m_size > 0) GC.RemoveMemoryPressure(m_size);//每个类提示实际取消内存.
                      Console.WriteLine("BigNativeResource destroy.");
                  }
              }

    2,设定有限制的资源----使用HandleCollector这个类的问题.

    private sealed class LimitedResource
            {
                private static readonly HandleCollector s_hc = new HandleCollector("LimitedResource", 2);
                public LimitedResource()
                {
                    s_hc.Add();
                    Console.WriteLine($"LimitedResource create {s_hc.Count}");
                }
                ~LimitedResource()
                {
                    s_hc.Remove();
                    Console.WriteLine("LimitedResource destroy. Count={0}", s_hc.Count);
                }
            }
    控制一个Handle Collector,并且初始化为2,也就是说只要该对象增加值达到2个,就开始垃圾回收.
    HandleCollectorDemo
    LimitedResource create 1
    LimitedResource create 2
    LimitedResource create 3
    LimitedResource create 4
    LimitedResource destroy. Count=4
    LimitedResource destroy. Count=3
    LimitedResource destroy. Count=2
    LimitedResource destroy. Count=1
    LimitedResource create 1
    LimitedResource create 2
    LimitedResource create 3
    LimitedResource create 4
    LimitedResource destroy. Count=4
    LimitedResource destroy. Count=3
    LimitedResource destroy. Count=2
    LimitedResource create 3
    LimitedResource create 3
    LimitedResource destroy. Count=2
    LimitedResource destroy. Count=1
    LimitedResource destroy. Count=0
    

    程序中改成了4个对象.也就是该类,通过控制数量来触发垃圾回收.


    15,终结的工作原理:

    有终结器的对象在垃圾回收的时候,从 Finalization Queue移动到Freachable Queue,然后执行Finalize方法,然后,在清空Freachale队列.所以,对于某个有终结器的对象,其有可能要求不止进行2次垃圾回收.


    16,手动监视和控制对象的生存期----CLR为每个AppDomain都提供了一个GC句柄表(GC Handle table)---该表使用

    GCHandle类在表中添加删除记录项.

    GCHandle 结构与 GCHandleType 枚举一起使用,以创建对应于任何托管对象的句柄。 此句柄可以是以下四种类型之一: WeakWeakTrackResurrectionNormalPinned。 如果分配了句柄,则在非托管客户端持有唯一引用时,可以使用它来防止垃圾回收器收集托管的对象。 如果没有这样的句柄,则在代表非托管客户端完成其工作之前,垃圾回收器可以收集该对象。

    你还可以使用 GCHandle 来创建一个固定的对象,该对象返回内存地址以防止垃圾回收器将对象移动到内存中。

    public struct GCHandle
        {
            public static GCHandle Alloc(object value);
            public static GCHandle Alloc(object value, GCHandleType type);
            //静态方法,用于将一个GCHandle-->IntPtr
            public static explicit operator IntPtr(GCHandle value);
            public static IntPtr ToIntPtr(GCHandle value);
            //静态方法,用于比较两个GCHandle
            public static bool operator ==(GCHandle a, GCHandle b);
            public static bool operator !=(GCHandle a, GCHandle b);
            //实列方法,用于释放表中的记录项,索引设为0---也就是将IntPtr设为0
            public void Free();
            //实列属性,用于获得记录项对象
            public object Target { get; set; }
            //判断索引是否被分配.索引不为0则返回true
            public bool IsAllocated { get; }//调用Alloc 为true,调用Free,为False
            //对于已固定记录项,这个方法返回对象地址
            public IntPtr AddOfPinnedObject();
    
        }

    GCHandleType 有以下四个类型:

    • Weak:允许监视生存期,Finalize对象可能执行,可能没执行.
    • WeakTrackResurrection:允许监视生存期,Finalize对象已执行.对象内存已回收
    • Normal:允许控制对象生存期(默认).该对象必须在内存里,垃圾回收时,可以移动.
    • Pinned:允许控制对象生存期(默认).该对象必须在内存里,垃圾回收时,不可移动,对象在内存中地址固定.

    垃圾


    回收流程:

    1GC标记所有可达对象,然后扫描GC句柄表,然后所有Normal和Pinned的记录项被看成根,所有的相关对象变为可达.不进行回收.
    2

    GC扫描句柄表中Weak记录项,如果其对象不可达,则将该记录项的引用值改为null.

    在终结器运行之前,Weak 引用归零,因此即使终结器使该对象复活,Weak 引用仍然是归零的。

    3GC扫描终结列表,将终结列表对象(带析构函数的类的对象)移动到freachable队列中,并且这些对象被标记,变成可达对象.
    4

    GC扫描GC句柄表WeakTrackResurrection记录项.如果其对象不可达,则记录项引用值更改为NULL

    该句柄类型类似于 Weak,但如果对象在终结过程中复活,此句柄不归零。

    5

    GC进行内存压缩,Pinned对象不会被移动




    GCHandle 使用情况-----Normal和Pinned入手

    使用 GCHandle.Alloc 方法注册对象,然后将返回的GcHandle实列转换为IntPtr...再将这个指针传递给本机代码.

    本机代码回调托管代码时,托管代码将传递的IntPtr转型为GCHandle,然后,查询其Target属性来获得托管对象的引用.

    本机代码不需要时,可以使用GCHandle.Free使其注销.

    使用Fixed…使垃圾回收的时候

    unsafe public static void Demo1()
            {
                for (int x = 0; x < 10000; x++) new object();
                IntPtr orgptr;
                byte[] bytes = new byte[1000];
                fixed(Byte* p = bytes)//告诉GC不要移动该对象.
                {
                    orgptr = (IntPtr)p;
                }
                GC.Collect();
                fixed(byte* p = bytes)//告诉GC不要移动该对象.
                {
                    Console.WriteLine(orgptr == (IntPtr)p ? "Yes" : "NO");
                }
            }
        }

    使用该关键字使得指向的对象地址固定,不被GC移动.(指针指向的对象地址必须固定)


    Weak Reference<T>---

    方法

    Equals(Object)

    确定指定对象是否等于当前对象。

    (继承自 Object)
    Finalize()

    丢弃对当前 WeakReference<T> 对象表示的目标的引用。

    GetHashCode()

    用作默认哈希函数。

    (继承自 Object)
    GetObjectData(SerializationInfo, StreamingContext)

    用序列化当前 SerializationInfo 对象所需的所有数据填充 WeakReference<T> 对象。

    GetType()

    获取当前实例的 Type

    (继承自 Object)
    MemberwiseClone()

    创建当前 Object 的浅表副本。

    (继承自 Object)
    SetTarget(T)

    设置 WeakReference<T> 对象引用的目标对象。

    ToString()

    返回表示当前对象的字符串。

    (继承自 Object)
    TryGetTarget(T)

    尝试检索当前 WeakReference<T> 对象引用的目标对象。

    本质是一个GCHandle包装器.其构造器调用Alloc方法.SetTarget设定Target对象.TryGetTarget用于获取Target对象.

    终结器调用Free方法.

    WeakReference<T>含义是,对于对象的引用时弱引用.也就是,其并不会阻止GC回收该对象.


    public static void Go()
            {
                ObjectA objA = new ObjectA("objA");
    
                WeakReference<ObjectA> weakReference = new WeakReference<ObjectA>(objA);
                ShowObject(weakReference);
               // weakReference.TryGetTarget(out ObjectA objectA1);
                objA = null;
                //objectA1 = null; 注释的时候,显示对象没有被回收,因为有引用,是能这句的时候,对象没有引用,所以下面的没有了.
               // GC.Collect();
    
                ShowObject(weakReference);
    
    
            }

    没有进行垃圾回收,显示

    obj1 is Exsit!
    obj1 is Exsit!

    有进行垃圾回收显示

    obj1 is Exsit!
    obj1 is not exsist!(垃圾回收后,该对象不存在了).


    17.使用ConditionalWeakTable

    [System.Runtime.InteropServices.ComVisible(false)]
    public sealed class ConditionalWeakTable<TKey,TValue> where TKey : class where TValue : class

    public static void GO()
            {
                var mc1 = new ManagedClass();
                var mc2 = new ManagedClass();
                var mc3 = new ManagedClass();
    
                var cwt = new ConditionalWeakTable<object, ClassData>();
                cwt.Add(mc1, new ClassData());
                cwt.Add(mc2, new ClassData());
                cwt.Add(mc3, new ClassData());
    
                var wr2 = new WeakReference(mc2);
                mc2 = null;
    
                GC.Collect();
                Print(wr2.Target, cwt);
               var b1= wr2.Target != null;
                //if (b1) Console.WriteLine("aaa");//注意,在if 语句里面,似乎这个弱引用被使用了???
            }
            class ManagedClass
            {
            }
    
            class ClassData
            {
                public DateTime CreationTime;
                public object Data;
    
                public ClassData()
                {
                    CreationTime = DateTime.Now;
                    this.Data = new object();
                }
            }
            private static void Print(object Target, ConditionalWeakTable<object, ClassData> cwt)
            {
                ClassData data = null;
    
                if (Target == null)
                    Console.WriteLine("No strong reference to mc2 exists.");
                else if (cwt.TryGetValue(Target, out data))
                    Console.WriteLine("Data created at {0}", data.CreationTime);
                else
                    Console.WriteLine("mc2 not found in the table.");
            }
        }

    -------这里有个关键问题.似乎在if中使用wr2,则这个对象不会被收集,所以,需要尽量在函数中使用wr2,否则其可能还存在.

    No strong reference to mc2 exists.
    答案2:如果If语句没有被注释.
    Data created at 2020/2/12 19:59:41
    aaa



  • 相关阅读:
    golang语言调试
    Debugging golang programs
    Goroutines vs Threads
    3dmax联机分布式渲染方法技巧详解
    VVDocumenter-Xcode
    Hallelujah Leonard Cohen
    创作思维导图的步骤
    XMind 快捷键完整命令
    圣经中有关天使的记载
    电箱吉他的电流声简单处理办法!
  • 原文地址:https://www.cnblogs.com/frogkiller/p/12300586.html
Copyright © 2011-2022 走看看