蛙蛙推荐:.net中的游标链表和普通链表对GC的影响

摘要:
对普通链表进行添加和删除操作,会创建和销毁对象,如果操作太频繁会对GC造成压力,而游标链表是事先分配好一个大数组,然后用数组的下标代替普通链表的引用指针,这样链表节点的添加删除,只是下标指向的改变,不会创建和销毁对象,相当于自己管理了内存,所以降低了GC的压力。
性能测试模型:
1、往链表里添加500w个int节点
2、把这500w个节点删除
3、重复1和2进行10次,分别记录各代gc回收次数,gc堆大小及执行时间。
性能测试结果
游标链表
err:0
gen:24,gen:15,gen:5
totalMemory:160217492
tack time:19358
.NET自带普通链表
gen:213,gen:125,gen:21
totalMemory:120221380
tack time:18024
性能测试结论:
对游标链表的频繁添加、删除操作引起的GC回收次数明显比普通链表要少的多,但游标链表因为使用了额外的数组记录空闲节点列表,所以占用内存大一些,由于每个标准操作的子步骤数比普通链表多一些,所以总体销号时间也稍大于普通链表,但不明显。
单元测试用例:
操作:
1、往链表的头上依次添加节点1,2,3加上默认的0节点共4个节点。
2、从链表的尾部连续删除两个节点
3、从链表的尾部开始,向前遍历节点,并打印节点数据
预期
输出数据2,3
单元测试结果:
通过
测试部分代码
class Program {
    
static void Main(string[] args) {
        
//UnitTest();

        
const int max = 500*10000;
        Stopwatch watch 
= Stopwatch.StartNew();
        
//TestLinkdList(max, 10); //这一句和下一句要分别测试,别同时测
        TestCursorList(max, 10);

        Console.WriteLine(
"gen:{0},gen:{1},gen:{2}",
            GC.CollectionCount(
0),GC.CollectionCount(1),GC.CollectionCount(2));
        Console.WriteLine(
"totalMemory:{0}",GC.GetTotalMemory(false));
        Console.WriteLine(
"tack time:{0}",watch.ElapsedMilliseconds);
        Console.ReadKey();
    }

    
private static void UnitTest() {
        CursorList
<int> list = new CursorList<int>(3);
        list.AddHeader(
1);
        list.AddHeader(
2);
        list.AddHeader(
3);
        list.RemoveTail();
        list.RemoveTail();
        CursorListNode
<int> node = list.Tail;
        
while (node != null//预期输出2,3
        {
            Console.WriteLine(node.Data);
            node 
= node.Next;
        }
    }

    
private static void TestCursorList(int max, int iteration)
    {
        CursorList
<int> list = new CursorList<int>(max);
        
int err = 0;
        
for (int k = 0; k < iteration; k++) {
            
for (int i = 0; i < max; i++)
                
if (list.AddHeader(i) == null) err++;
            
for (int i = 0; i < max; i++)
                list.RemoveTail();
        }
        Console.WriteLine(
"err:{0}", err);
    }
    
private static void TestLinkdList(int max, int iteration) {
        LinkedList
<int> list = new LinkedList<int>();
        
        
for (int k = 0; k < iteration; k++) {
            
for (int i = 0; i < max; i++)
                list.AddFirst(i);
            
for (int i = 0; i < max; i++)
                list.RemoveLast();
        }
    }
}

游标链表实现代码
为了简单起见,只是先了在头部添加和移除尾部节点的方法。
public class CursorListNode<T> {
    
public CursorListNode<T> Next { getset; }
    
public CursorListNode<T> Prior { getset; }
    
internal int Index { getset; }
    
public T Data { getset; }
}
public class CursorList<T> {
    
private readonly Queue<int> _freeQ = null;
    
private readonly CursorListNode<T>[] _list = null;

    
public CursorList(int capacity) {
        _freeQ 
= new Queue<int>(capacity+1);
        _list 
= new CursorListNode<T>[capacity+1];
        
        _list[
0= Header = Tail = new CursorListNode<T> {
            Index 
= 0,
            Data 
= default(T),
            Next 
= null,
            Prior 
= null
        };

        
for (int i = 1; i < capacity+1; i++)
        {
            _freeQ.Enqueue(i);
            _list[i] 
= new CursorListNode<T> {
                Index 
= -1,
                Data 
= default(T),
                Next 
= null,
                Prior 
= null
            };
        }
    }
    
public CursorListNode<T> AddHeader(T data) {
        
if(_freeQ.Count < 1return null;
        
int newIndex = _freeQ.Dequeue();

        CursorListNode
<T> newNode = _list[newIndex];
        newNode.Index 
= newIndex;
        newNode.Data 
= data;
        newNode.Next 
= null;
        newNode.Prior 
= Header;

        Header.Next 
= newNode;
        Header 
= newNode;

        
return newNode;
    }

    
public void RemoveTail() {
        
if(Tail == nullreturn;
        _freeQ.Enqueue(Tail.Index);
        
if (Tail.Next != null)
        {
            Tail 
= _list[Tail.Next.Index];
            Tail.Prior 
= null;
        }
        
else
            Tail 
= null;
        
    }

    
public CursorListNode<T> Header { getset; }
    
public CursorListNode<T> Tail { getset; }
}

参考链接:
CursorList.cpp - Implementation for cursor linked list
http://www.cplusplus.happycodings.com/Data-Structures-and-Algorithm-Analysis-in-C++/code33.html
链表的游标法实现 cursor_list
http://blog.csdn.net/liuzongqiang/archive/2008/01/06/2027762.aspx
posted @ 2009-08-01 00:48  蛙蛙王子  Views(2833)  Comments(11Edit  收藏  举报