leveldb有一个简单内存分配器Arena,代码很简单(也许是leveldb最简单的部分了),对外也只是提供了2个接口:

  • 请求返回给定大小的内存
  • 返回当前内存分配器所有分配的内存总量

Arena对象在销毁的时候会把所有该对象分配的内存delete掉。

这个内存分配器只在一个地方被用到-skiplist,用于生成新节点的时候分配内存。 问题来了,为什么需要这么一个分配器,直接用new不行么? 答案是可以的,不过相对于直接使用new分配内存,使用Arena有几个好处。 第一个好处是减少内存碎片 Arena内部每次按照4k的大小分配,外部请求是在这个4k的block里面分配的,这样可以有效减少小内存的分配进而减少内存碎片。不过想了一下,系统实现的new,delete本身也是有类似的功能,这个不算什么实质性的好处。

第二个好处是内存的自动释放 首先上面提到过Arena只是在skiplist里面用到,skiplist这个类没有实现特定的析构函数,所以在销毁的时候不会做内存的清理操作,内存清理操作是由Arena负责的。当Arena对象销毁时,所有它之前创建的内存都会被释放,这就进而把skiplist占用的内存释放了。如果skiplist的内存是按照new来分配的,这时候我们要销毁整个skiplist的话,就需要从头到尾遍历一下整个链表,然后依次释放每个节点的内存,对比使用Arena高下立判。这应该是skiplist使用Arena的最主要原因。

第三个好处是统计当前已分配的内存 前面提到Arena的接口已经说到过。使用new也可以达到同样的结果。

使用Arena有什么缺点呢 第一是有可能造成内存浪费。 由于每次按4k的block分配,可能会出现当前剩余的内存不够请求的数目,这时候就要浪费掉这部分内存然后去申请一块新内存。不过系统new应该也有这种情况。

第二是没有提供删除的接口。 skiplist本身没有提供删除节点的方法,所以Arena也没有删除节点的接口。如果skiplist需要提供删除的接口,当前的Arena是无法做到的,想要提供删除接口可能会极大的增加代码的复杂度。在这一点上系统new则可以直接delete。