Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/wix/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何实现简单的线程安全内存管理以防止碎片?_C#_Memory Management - Fatal编程技术网

C# 如何实现简单的线程安全内存管理以防止碎片?

C# 如何实现简单的线程安全内存管理以防止碎片?,c#,memory-management,C#,Memory Management,我需要实现简单的线程安全内存管理,以防止碎片。我读过一些关于这方面的文章,比如和,但我不知道如何在C#中开始实现 要点:95%的内存分配请求将是小于1K的块 谁能先给我一些代码吗 已编辑 我已经编写了一个分配器,但是我没有在Alloc方法中使用池。如何更改它以使其使用池 class Allocator { private int _id; //TODO: it must be struct! class Block {

我需要实现简单的线程安全内存管理,以防止碎片。我读过一些关于这方面的文章,比如和,但我不知道如何在C#中开始实现

要点:95%的内存分配请求将是小于1K的块

谁能先给我一些代码吗

已编辑 我已经编写了一个
分配器
,但是我没有在
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>();
}