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

时间:2012-04-17 06:58:43

标签: c# memory-management

我需要实现简单的线程安全内存管理,以防止碎片。我已经阅读了一些关于thisthis的文章,但我无法弄清楚如何在C#中开始实现。

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

有人可以给我一些代码吗?

EDITED 我写了一个Allocator但是我没有在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;
       }
}

1 个答案:

答案 0 :(得分:1)

如果您的.NET应用程序确实需要自定义内存管理器,那么您应该从非托管世界(您的第二个链接)关注提示(或简单地翻译代码)

.NET环境中的内存分配是完全不同的,内存分散得更多(因为默认分配器特权分配速度)但是它可以被压缩(因此内存碎片问题不是真正的问题)。

不是你的情况,但是大对象(这些天这个阈值设置为85 KB)将分配不同的策略,并且它们不会被压缩。我认为你可能只有在你创造了许多短生命大对象时才需要自定义分配器。

第一个链接提供了一个非常简单的实现,您是否在多线程环境中配置文件?在您的情况下,您确定它的性能是否优于默认分配?即使它表现得更好,你确定你需要它吗?

为了使您的内存分配器线程安全,您可以为每个线程使用不同的堆,或者只是锁定您的数据结构(例如,如果您将可用内存块列表保留在LinkedList中,则可以在删除节点时锁定该结构从列表中)。这不是一个可以用几行解释的主题,如果你真的对这些内部感兴趣,你可以阅读伟大的“CLR via C#”一书。

当对象分配真的很广泛时,你可以使用复活机制来增加你的对象,但这增加了必须要评估的复杂性,通常你付出的代价会更大。您可以从以下工厂方法开始:

MyObject obj = ObjectFactory.Allocate();

而不是简单

MyObject obj = new MyObject();

通过这种方式,如果你真的需要它,你可以切换到别的东西但是......
......小提示:不要使用内存分配,如果您不确定自己在做什么以及 PROFILED 当前的内存分配策略之后。

(我很想在这条消息中使用更大的字体)

这可能是您可以对应用程序执行的最糟糕的事情之一,因为您会使速度变慢并且代码的可读性会降低。 99.999%的应用程序不需要这些自定义内容,您确定您的应用程序需要吗?

修改
从这个例子来看,你所做的并不是很清楚。您的Alloc方法返回一个ID但是如何获取分配的数据?反正...
如果你真的需要做那样的话......

  • 不要保留字节列表,你只会浪费内存。
  • 不提供Free方法,你在.NET中,所以请依靠GC。
  • 保留可用块列表(Block对象)。在Allocate方法中,您将在空闲块列表中搜索所需大小的块。如果找到它,则返回该块,然后将其从列表中删除。如果您没有找到该块,则必须将其分配并简单地将其返回给调用者。
  • Block对象的终结器中调用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>();
}

请注意:

  • 此实现完全没有 高效(在这种情况下,更好的解决方案可能是ReadWriterLockSlim而不是lock或其他更合适的数据结构,而不是{{1 }})。
  • 使用枚举的搜索是可怕的,但这只是为了清晰起见。
  • 为每个对象添加终结器可能会降低性能。
  • 该示例使用List<T>作为所需数据的容器(字节数组)。这就是你需要的吗?

那说我仍然认为在你花费任何时间之前,你应该检查你是否需要它。您的应用程序是否遇到此问题?这是问题吗?想象一下,例如,您有一个数据处理应用程序。您的管道由以下阶段组成:

  • 收购(以某种方式定期获取数据)。
  • 处理(各种过滤器)。
  • 可视化。

如果为每个数据包分配一个新缓冲区,则可能会创建许多小对象。我不认为这可能是一个问题,但你可能会考虑在采集阶段重用相同的(预先分配的)缓冲区,而不是试图为所有应用程序增加复杂性。

我希望我的意思很清楚。