导航

.net 内存常见问题

GC 内存常见问题

1.       GC 对象的复活

namespace ConsoleApplication2
{
    public class Resurrection
    {
        public int Data;
        public Resurrection(int data)
        {
            this.Data = data;
        }
     // finalizer
~Resurrection() { Program.Instance = this; } } class Program { public static Resurrection Instance; static void Main(string[] args) { Instance = new Resurrection(1);
int gen = -1; gen = GC.GetGeneration(Instance); Instance = null; GC.Collect(); GC.WaitForPendingFinalizers(); //object is alive again,and generation is promoted to 1. gen = GC.GetGeneration(Instance); Console.WriteLine(Instance.Data); Instance = null; GC.Collect(); } } }

 

1.1   执行Collect, 检查引用,对象引用没有了。

1.2   创建实例时已经在Finalizer表上做了记录,所以我们检查到了对象有Finalizer.

1.3   因为有Finalizer, 我们将记录一道Finalizer2表上。

1.4   在Finalizer2表上有记录,所以不释放内存。并提升代龄

1.5   Collect执行完毕。GC.WaitForPendingFinalizers, 我们将等待Finalizer2表上的Finalizers的执行。

1.6   Finalizer执行后我们的Instance 复活了。

1.7   再一次去除所有的引用。

1.8   执行Collect.检查引用,没问题。

1.9   由于上次已经冲Finalizer表里删除,所有这次没有查到。

1.10 释放内存。

 

2.       非托管资源的释放。

    public class Base : IDisposable
    {
        public void Dispose()
        {
            this.Dispose(true);
            // Remove this from the Finalizer list.
            GC.SuppressFinalize(this);
        }
protected virtual void Dispose(bool disposing) { if (disposing) { // managed resource } // unmanaged resource }
~Base() { this.Dispose(false); } }

如果finalizer执行过久会影响GC的性能。所以最好是在Using来把资源释放,然后从Finalizer表里移除.

  

3.  弱引用。

    public class Fat
    {
        public int Data;
        public Fat(int data)
        {
            this.Data = data;
        }
    } 

    class Program
    {
        static void Main(string[] args)
        {
            Fat oFat = new Fat(1);
            WeakReference oFatRef = new WeakReference(oFat);
            oFat = null;
            if (oFatRef.IsAlive)
            {
                Console.WriteLine(((Fat)oFatRef.Target).Data);
            }

            GC.Collect();
        }
    }

 对于胖对象,通过弱引用,可以是对象有段存活时间,此时可以使用它,但是也可能被在内存不够或强制的时候被回收。

 

4. GC具体做了哪些工作?

  • 在标记阶段找到并创建一个所有存活对象的链表。
  • 在重定位阶段更新要压缩对象的引用。应该是移动对象的位置,让内存紧凑的排列。
  • 在压缩阶段回收死对象的内存空间,并把存活的对象紧凑的排列,来更有效的使用内存。

  一般来说,大对象(>= 85000 byte)不会被压缩,否则会影响性能。但是从.net 4.5.1开始我们可以通过GCSettings.LargeObjectHeapCompactionMode 来压缩大对象。 

  垃圾回收器通过以下的信息来判断对象是否存活:

  •     栈根(stack root).
  •   垃圾收集句柄(Garbage collection handles). 由用户或者CLR声明的指向托管对象的句柄对象。
  •   静态数据.

在垃圾收集开始前,处理跑GC的线程,所有的托管线程被暂停。具体看下图:

 

参考资料:

 

https://msdn.microsoft.com/en-us/library/ee787088(v=vs.110).aspx#what_happens_during_a_garbage_collection

posted on 2015-05-17 20:41  水中游  阅读(146)  评论(0)    收藏  举报