C# 如何实现简单的线程安全内存管理以防止碎片?
我需要实现简单的线程安全内存管理,以防止碎片。我读过一些关于这方面的文章,比如和,但我不知道如何在C#中开始实现 要点:95%的内存分配请求将是小于1K的块 谁能先给我一些代码吗 已编辑 我已经编写了一个C# 如何实现简单的线程安全内存管理以防止碎片?,c#,memory-management,C#,Memory Management,我需要实现简单的线程安全内存管理,以防止碎片。我读过一些关于这方面的文章,比如和,但我不知道如何在C#中开始实现 要点:95%的内存分配请求将是小于1K的块 谁能先给我一些代码吗 已编辑 我已经编写了一个分配器,但是我没有在Alloc方法中使用池。如何更改它以使其使用池 class Allocator { private int _id; //TODO: it must be struct! class Block {
分配器
,但是我没有在Alloc
方法中使用池。如何更改它以使其使用池
class Allocator
{
private int _id;
//TODO: it must be struct!
class Block
{
public int offset;
public int blockLength;
}
private readonly Dictionary<int, Block> idToBlock = new Dictionary<int, Block>();
private List<byte> rawData;
private int allocatedBlocksLength;
// sync
private readonly object _sync = new object();
public int Alloc(int count)
{
rawData.AddRange(new byte[count]);
idToBlock.Add(_id, new Block { offset = allocatedBlocksLength, blockLength = count });
var blockId = _id;
++_id;
allocatedBlocksLength += count;
return blockId;
}
public void Free(int id)
{
// Search in table
Block block;
if (!idToBlock.TryGetValue(id, out block))
return;
// remove object and update all offsets that after our block
foreach (var kv in idToBlock)
{
if (kv.Key == id)
continue;
if (kv.Value.offset > block.offset)
continue;
// changing indexes
kv.Value.offset -= block.blockLength;
}
// update how much left
allocatedBlocksLength -= block.blockLength;
}
}
类分配器
{
私人内部id;
//TODO:它必须是struct!
类块
{
公共整数偏移;
公共整数块长度;
}
私有只读词典idToBlock=新词典();
私有列表数据;
专用int分配块长度;
//同步
私有只读对象_sync=新对象();
公共整数分配(整数计数)
{
AddRange(新字节[计数]);
Add(_id,新块{offset=allocatedBlocksLength,blockLength=count});
var blockId=\u id;
++_身份证;
allocatedBlocksLength+=计数;
返回blockId;
}
公共无效(内部id)
{
//在表中搜索
块;
如果(!idToBlock.TryGetValue(id,out block))
返回;
//删除对象并更新块之后的所有偏移
foreach(idToBlock中的var kv)
{
如果(千伏键==id)
继续;
if(千伏值偏移>块偏移)
继续;
//改变索引
kv.Value.offset-=块体长度;
}
//更新剩下多少
allocatedBlocksLength-=block.blockLength;
}
}
如果您的.NET应用程序确实需要一个自定义内存管理器,您不应该遵循非托管世界(您的第二个链接)中的提示(或简单地翻译代码)
NET环境中的内存分配是完全不同的,内存碎片化程度更高(因为默认的分配器特权分配速度),但它可以被压缩(因此内存碎片化的问题在那里不是一个真正的问题)
这不是您的情况,但是大对象(现在这个阈值设置为85 KB)将使用不同的策略分配,并且不会被压缩。我认为,只有在创建大量短期大对象的情况下,才可能需要自定义分配器
第一个链接提供了一个非常简单的实现,您是否在多线程环境中配置了它?您是否确定在您的情况下,它的性能优于默认分配?即使它的性能稍微好一点,你确定你需要它吗
为了使内存分配器线程安全,可以为每个线程使用不同的堆,或者只锁定数据结构(例如,如果将可用内存块列表保留在LinkedList中,则可以在从列表中删除节点时锁定结构)。这不是一个可以用几行文字来解释的主题,如果你真的对这些内部内容感兴趣,你可以阅读伟大的《CLR通过C#》一书
当对象分配非常广泛时,您可以为您的对象使用复活机制,但这增加了许多必须评估的复杂性,通常您将付出更大的代价。您可以从以下工厂方法开始:
MyObject obj = ObjectFactory.Allocate();
而不是简单的:
通过这种方式,如果您确实需要,您可以切换到其他设备,但……一个小提示:如果您不确定自己正在做什么,并且在分析了当前的内存分配策略后,请不要玩内存分配 (我很想用更大的字体来写这封信) 这可能是对应用程序所做的最糟糕的事情之一,因为这样会使应用程序变得更慢,代码的可读性也会降低。99.999%的应用程序不需要这些自定义内容,您确定应用程序需要这些自定义内容吗 编辑
从这个例子来看,你并不清楚你在做什么。Alloc方法返回一个ID,但如何获得分配的数据?无论如何…
如果你真的需要做这样的事情…
- 不要保留字节列表,这样只会浪费内存
- 不要提供
方法,因为您在.NET中,所以请依赖GC免费的
- 保留可用的块的列表(
对象)。在块
方法中,您将在空闲块列表中搜索所需大小的块。如果找到它,则返回该块并将其从列表中删除。如果找不到块,则必须分配它并将其返回给调用方Allocate
- 在
对象调用GC.ReRegisterForFinalize方法并将对象插入可用块列表中块的终结器中
非常简单的实现,作为一个例子而不是一个真正的程序:
sealed class Block
{
internal Block(int size)
{
Data = new byte[size];
}
~Block()
{
BlockFactory.Free(this);
GC.ReRegisterForFinalize(this);
}
public byte[] Data
{
get;
private set;
}
}
static class BlockFactory
{
public static Block Allocate(int size)
{
lock (_freeBlocks)
{
foreach (Block block in _freeBlocks)
{
if (block.Data.Length == size)
{
_freeBlocks.Remove(block);
return block;
}
}
return new Block(size);
}
}
internal static void Free(Block block)
{
lock (_freeBlocks) _freeBlocks.Add(block);
}
private static List<Block> _freeBlocks = new List<Block>();
}
密封类块
{
内部块(整数大小)
{
数据=新字节[大小];
}
~Block()
{
BlockFactory.Free(这个);
GC.重新登记以最终确定(本);
}
公共字节[]数据
{
得到;
私人设置;
}
}
静态类块工厂
{
公共静态块分配(整数大小)
{
锁定(_自由块)
{
foreach(块中的块_freeBlocks)
{
if(block.Data.Length==大小)
{
_删除(块);
返回块;
}
}
返回新块(大小);
sealed class Block
{
internal Block(int size)
{
Data = new byte[size];
}
~Block()
{
BlockFactory.Free(this);
GC.ReRegisterForFinalize(this);
}
public byte[] Data
{
get;
private set;
}
}
static class BlockFactory
{
public static Block Allocate(int size)
{
lock (_freeBlocks)
{
foreach (Block block in _freeBlocks)
{
if (block.Data.Length == size)
{
_freeBlocks.Remove(block);
return block;
}
}
return new Block(size);
}
}
internal static void Free(Block block)
{
lock (_freeBlocks) _freeBlocks.Add(block);
}
private static List<Block> _freeBlocks = new List<Block>();
}