1、将比较大的对象分割成较小的对象,使每个小对象大小小于85, 000字节,从而不再分配在LOH上;

  2、尽量“重用”少量的大对象,而不是分配很多大对象;

  3、每隔一段时间重启一下程序。

  终我们发现,我们的软件中使用数组(List<float>)保存了一些曲线数据,而这些曲线的大小很可能会超过了85, 000字节,同时曲线对象的个数也非常多,从而对LOH造成了很大的压力,甚至出现了文章开头所描述的情况。针对这一情况,我们采用了策略1的方法,定义了一个类似C++中deque的数据结构,它以分块内存的方式存储数据,而且保证每一块的大小都小于85, 000,从而解决了这一问题。

  此外要说的是,不要以为64位环境中可以忽略这一问题。虽然64位环境下有更大的内存空间,但对于操作系统来说,.NET中的LOH会提交很大范围的内存区域,所以当存在大量的内存空隙时,即使不会出现OutOfMemoryException异常,也会使得内页页面交换的频率不断上升,从而使软件运行的越来越慢。

  后分享我们定义的分块列表,它对IList<T>接口的实现行为与List<T>相同,代码中只给出了比较重要的几个方法。

    public class BlockList<T> : IList<T> 
     { 
         private static int maxAllocSize; 
         private static int initAllocSize; 
         private T[][] blocks; 
         private int blockCount; 
         private int[] blockSizes; 
         private int version; 
         private int countCache; 
         private int countCacheVersion; 
      
         static BlockList() 
         { 
             var type = typeof(T); 
             var size = type.IsValueType ? Marshal.SizeOf(default(T)) : IntPtr.Size; 
             maxAllocSize = 80000 / size; 
             initAllocSize = 8; 
         } 
      
         public BlockList() 
         { 
             blocks = new T[8][]; 
             blockSizes = new int[8]; 
             blockCount = 0; 
         } 
      
         public void Add(T item) 
         { 
             int blockId = 0, blockSize = 0; 
             if (blockCount == 0) 
             { 
                 UseNewBlock(); 
             } 
             else
             { 
                 blockId = blockCount - 1; 
                 blockSize = blockSizes[blockId]; 
                 if (blockSize == blocks[blockId].Length) 
                 { 
                     if (!ExpandBlock(blockId)) 
                     { 
                         UseNewBlock(); 
                         ++blockId; 
                         blockSize = 0; 
                     } 
                 } 
             } 
      
             blocks[blockId][blockSize] = item; 
             ++blockSizes[blockId]; 
             ++version; 
         } 
      
         public void Insert(int index, T item) 
         { 
             if (index > Count) 
             { 
                 throw new ArgumentOutOfRangeException("index"); 
             } 
      
             if (blockCount == 0) 
             { 
                 UseNewBlock(); 
                 blocks[0][0] = item; 
                 blockSizes[0] = 1; 
                 ++version; 
                 return; 
             } 
      
             for (int i = 0; i < blockCount; ++i) 
             { 
                 if (index >= blockSizes[i]) 
                 { 
                     index -= blockSizes[i]; 
                     continue; 
                 } 
      
                 if (blockSizes[i] < blocks[i].Length || ExpandBlock(i)) 
                 { 
                     for (var j = blockSizes[i]; j > index; --j) 
                     { 
                         blocks[i][j] = blocks[i][j - 1]; 
                     } 
      
                     blocks[i][index] = item; 
                     ++blockSizes[i]; 
                     break; 
                 } 
      
                 if (i == blockCount - 1) 
                 { 
                     UseNewBlock(); 
                 } 
      
                 if (blockSizes[i + 1] == blocks[i + 1].Length 
                     && !ExpandBlock(i + 1)) 
                 { 
                     UseNewBlock(); 
                     var newBlock = blocks[blockCount - 1]; 
                     for (int j = blockCount - 1; j > i + 1; --j) 
                     { 
                         blocks[j] = blocks[j - 1]; 
                         blockSizes[j] = blockSizes[j - 1]; 
                     } 
      
                     blocks[i + 1] = newBlock; 
                     blockSizes[i + 1] = 0; 
                 } 
      
                 var nextBlock = blocks[i + 1]; 
                 var nextBlockSize = blockSizes[i + 1]; 
                 for (var j = nextBlockSize; j > 0; --j) 
                 { 
                     nextBlock[j] = nextBlock[j - 1]; 
                 } 
      
                 nextBlock[0] = blocks[i][blockSizes[i] - 1]; 
                 ++blockSizes[i + 1]; 
      
                 for (var j = blockSizes[i] - 1; j > index; --j) 
                 { 
                     blocks[i][j] = blocks[i][j - 1]; 
                 } 
      
                 blocks[i][index] = item; 
                 break; 
             } 
      
             ++version; 
         } 
      
         public void RemoveAt(int index) 
         { 
             if (index < 0 || index >= Count) 
             { 
                 throw new ArgumentOutOfRangeException("index"); 
             } 
      
             for (int i = 0; i < blockCount; ++i) 
             { 
                 if (index >= blockSizes[i]) 
                 { 
                     index -= blockSizes[i]; 
                     continue; 
                 } 
      
                 if (blockSizes[i] == 1) 
                 { 
                     for (int j = i + 1; j < blockCount; ++j) 
                     { 
                         blocks[j - 1] = blocks[j]; 
                         blockSizes[j - 1] = blockSizes[j]; 
                     } 
      
                     blocks[blockCount - 1] = null; 
                     blockSizes[blockCount - 1] = 0; 
                     --blockCount; 
                 } 
                 else
                 { 
                     for (int j = index + 1; j < blockSizes[i]; ++j) 
                     { 
                         blocks[i][j - 1] = blocks[i][j]; 
                     } 
      
                     blocks[i][blockSizes[i] - 1] = default(T); 
                     --blockSizes[i]; 
                 } 
      
                 break; 
             } 
      
             ++version; 
         } 
      
         private bool ExpandBlock(int blockId) 
         { 
             var length = blocks[blockId].Length; 
             if (length == maxAllocSize) 
             { 
                 return false; 
             } 
      
             length = Math.Min(length * 2, maxAllocSize); 
             Array.Resize(ref blocks[blockId], length); 
             return true; 
         } 
      
         private void UseNewBlock() 
         { 
             if (blockCount == blocks.Length) 
             { 
                 Array.Resize(ref blocks, blockCount * 2); 
                 Array.Resize(ref blockSizes, blockCount * 2); 
             } 
      
             blocks[blockCount] = new T[initAllocSize]; 
             blockSizes[blockCount] = 0; 
             ++blockCount; 
         } 
     }