• 高性能-GC3


    带着问题去思考!大家好

    今天我们继续优化。

    避免对象固定

      对象固定(Pinning)是为了能够安全地将托管内存的引用传递给本机代码,最常见的用处就是 传递数组和字符串。如果不与本机代码进行交互,就完全不应该有对象固定的需求。

    对象固定会把内存的地址固定下来, 垃圾回收器就无法移动这些对象,会增加内存碎片的可能,垃圾回收器会记住那些被固定的对象,以便能利用固定对象之间的空闲内存。过多的情况,还会导致内存碎片的产生和内存堆的扩大

      对象固定既可能是显式的,也可能是隐式的,使用GCHandleType.Pinned类型的GCHandle或者fixed关键字,可以完成显式对象固定,代码块必须标记为unsafe。用fixed/using用起来更方便,(fixed和GCHandle之间的区别类似于using和显式调用Dispose的差别),异步环境下是无法使用的,因为异步状态不能传递handle,也不能在回调方法中销毁handle。

    避免使用终结方法

      非必要情况下,永远不要实现终结方法(Finalizer).终结方法是一段由垃圾回收器引发调用的代码,用于清理非托管资源。终结方法由一个独立的线程调用,排成队列依次完成,而且只有依次垃圾回收之后,对象被垃圾回收器声明已销毁,才会进行调用。如果类实现了终结方法,对象就一定会滞留在内存中,即便在垃圾回收时应该被销毁的情况下。

      如果实现了终结方法,那就必须同时实现IDisposable接口以启用显示清理,还要在Dispise方法中调用GC.SuppressFinalize(this)来吧对象从移除终结队列中移除。只要能在下次垃圾回收之前调用Dispose,就可以适时把对象清理干净。

      

      

    class Foo:IDisposeable
    {
       Foo(){Dispose(false);}
       public void Dispose()
       {
          Dispose(true);
          GC.SuppressFinalize(this);
        }  
       protected virtual void Dispose(bool disposing)
       {
         if(disposing)
         {
           this.manageResoure.Dispose();
          }
         //清理非托管资源
          UnsafeClose(this.handle);
       }
    }

    避免分配大对象

    大对象的界限被为85000字节,任何大于这个值的对象都被认为是大对象,并独立的内存堆中进行分配,尽量避免,不仅是因为LOH的垃圾回收开销大,更多原因是因为内存碎片会导致内存用量不断增长

    避免缓冲区复制

    任何时候都应该避免复制数据,比如你已经把文件数据读入了MemorySteam(如果需要较大的缓存区,最好是用池化的流),一旦内存分配完毕,就应把此MemoryStream视为只读流,所有需要访问的MemoryStream的组件都能从同一份数据备份中读取数据,

    如果需要表示整个缓冲区的一段,请使用ArraySegment<T>类,可用来代表底层byte[]类型缓冲区的一部分区域。此ArraySegment可以传给API,而与原来的流无关,甚至可以被绑定到一个新的MemoryStream对象上,这些过程都不会发生数据复制。

    var memoryStream=new MemoryStream();
    var segment=new ArraySegment<byte>(memoryStream.GetBuffer(),100,1024);
    ....
    var blockStream=new MemoryStream(segment.Array,segment.Offset,segment.Count);

    内存复制造成的最大影响肯定不是CPU,而是垃圾回收。

     对长期存活对象和大型对象进行池化

    对象要么转瞬即逝,要么一直存活;要么在第0代垃圾回收时消失,要么就在第2代内存堆中一直留下去。有些对象基本上是静态的,伴随程序自然诞生,并在程序生存期间保持存活,还有一些对象看不出有一直存活的必要。但它们在程序上下文中体现出来的生存期,决定了它们会历经第0代(或者1代)垃圾回收并仍然存活。这时候应该考虑对这类对象进行池化。还有LOH中分配的对象,典型例子就是集合类对象,

    .NET已提供了一种针对受限托管资源的处理模式--IDisposable模式。比较合理的设计就是派生一个新类型并实现IDisposable接口,在Dispose方法中将池化对象归还共享池(Pool)。

     public interface IPoolableObject:IDisposable
        {
            int Size { get; }
            void Reset();
            void SetPoolManager(PoolManager poolManager);
        }
    public class PoolManager
        {
            private class Pool
            {
                public int PooledSize { get; set; }
                public int Count { get { return this.Stack.Count; } }
    
                public Stack<IPoolableObject> Stack { get; private set; }//泛型容器
                public Pool()
                {
                    this.Stack = new Stack<IPoolableObject>();
                }
    
            }
            const int MaxSizePerType = 10 * (1 << 10);//10MB
            Dictionary<Type, Pool> pools = new Dictionary<Type, Pool>();
            public int TotalCount
            {
                get
                {
                    int sum = 0;
                    foreach (var pool in this.pools.Values)
                    {
                        sum += pool.Count;
                    }
                    return sum;
                }
            }
            public T GetObject<T>()
                where T : class, IPoolableObject, new()
            {
                Pool pool;
                T valueToReturn = null;
                if(pools.TryGetValue(typeof(T),out pool))
                {
                    if(pool.Stack.Count>0)
                    {
                        valueToReturn = pool.Stack.Pop() as T;
                    }
                }
                if(valueToReturn==null)
                {
                    valueToReturn = new T();
                }
                valueToReturn.SetPoolManager(this);//对象池
                return valueToReturn;
            }
            public void ReturnObject<T>(T value) where T : class, IPoolableObject, new()
            {
                Pool pool;
                if (pools.TryGetValue(typeof(T), out pool))
                {
                    pool = new Pool();
                    pools[typeof(T)] = pool;
                }
                if(value.Size+pool.PooledSize<MaxSizePerType)
                {
                    pool.PooledSize += value.Size;
                    value.Reset();
                    pool.Stack.Push(value);
                }
            }
            class MyObject:IPoolableObject
            {
                private PoolManager poolManager;
                public byte[] Data { get; set; }
                public int UsableLength { get; set; }
    
                public int Size
                {
                    get { return Data!=null?Data.Length:0}
                }
                void IPoolableObject.Reset()
                {
                    UsableLength = 0;
                }
                void IPoolableObject.SetPoolManager(PoolManager poolManager)
                {
                    this.poolManager = poolManager;
                }
                public void Dispose()
                {
                    this.poolManager.ReturnObject(this);
                }
            }
        }
    View Code

    被池化对象必须要实现自定义接口,需要有点工作量。为了实现池化和对象重用,必须完全了解并掌握这些对象。因为在每次把池化对象归还共享池时,你的代码必须把对象重置为已知的,安全的状态,

    共享池中的对象永远不会被销毁,这与内存泄漏难以区分开。通常不要把池化作为默认解决方案,主要处理一些LOH分配。能消除99%的LOH问题。这类对象就是MemorySteam,我们用它来序列化并通过网络传递数据。

    减少LOH的碎片整理

    如果做不到完全避免LOH,就尽力避免碎片整理,

    保证LOH的每次分配都是同一尺寸的,或者至少有几种标准尺寸的组合。比如LOH的一种常规用途就是缓冲区池,不能让各个缓冲区大小不易,而应该所有缓冲区都是相同尺寸,或者固定大小(1MB),如果某一块缓冲区确实需要被垃圾回收了,那么下一次分配的缓冲区就有很大概率会落在这块空闲内存上。而不会放到堆的末尾去。

     

  • 相关阅读:
    重力感应GSensor 方向介绍
    php图片保存、下载
    AJAX技术在PHP开发中的简单应用
    php 面向对象基础
    用PHP处理多个同名复选框
    去掉codeigniter地址中的index.php
    PHP中如何运用ini_set和ini_get()
    Windows 7下PHP配置环境
    zend_application 说明
    PHP写的域名查询系统whois
  • 原文地址:https://www.cnblogs.com/ccaa/p/12578552.html
Copyright © 2020-2023  润新知