.NET陷阱之大对象堆问题
作者:网络转载 发布时间:[ 2013/4/19 10:32:04 ] 推荐标签:
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;
}
}
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11