D 的 gc, 哪位大大比较清楚, 请解惑

redsea 2007-09-11
看了看 tango gcx.d 里面的 mark 函数, 扫描一段内存的时候, 似乎将里面每个值(32bit 下, 就是每连续4个字节了) 都当指针, 查找对应这个值, 有没有登记在册的内存块, 有的话, 认为这是指针 ?

应该没有这么蛮力吧?  --- 这样蛮力的话, 将做无法实现内存块搬移

哪位大大比较清除的, 请解解惑, 谢谢啦.


    void mark(void *pbot, void *ptop)
    {
        void **p1 = cast(void **)pbot;
        void **p2 = cast(void **)ptop;
        uint changes = 0;

    //printf( "marking range: %p -> %p\n", pbot, ptop );
        //if (log) debug(PRINTF) printf("Gcx::mark(%x .. %x)\n", pbot, ptop);
        for (; p1 < p2; p1++)
        {
            Pool *pool;
            byte *p = cast(byte *)(*p1);

            //if (log) debug(PRINTF) printf("\tmark %x\n", p);
            if (p >= minAddr)
            {
                pool = findPool(p);
                if (pool)
                {
                    size_t offset = cast(uint)(p - pool.baseAddr);
                    uint biti;
                    uint pn = offset / PAGESIZE;
                    Bins bin = cast(Bins)pool.pagetable[pn];

                    //debug(PRINTF) printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti);

                    // Adjust bit to be at start of allocated memory block
                    if (bin <= B_PAGE)
                    {
                        biti = (offset & notbinsize[bin]) >> 4;
                        //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
                    }
                    else if (bin == B_PAGEPLUS)
                    {
                        do
                        {   --pn;
                        } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
                        biti = pn * (PAGESIZE / 16);
                    }
                    else
                    {
                        // Don't mark bits in B_FREE or B_UNCOMMITTED pages
                        continue;
                    }

                    //debug(PRINTF) printf("\t\tmark(x%x) = %d\n", biti, pool.mark.test(biti));
                    if (!pool.mark.test(biti))
                    {
                        //if (log) debug(PRINTF) printf("\t\tmarking %x\n", p);
                        pool.mark.set(biti);
                        if (!pool.noscan.test(biti))
                        {
                            pool.scan.set(biti);
                            changes = 1;
                        }
                        log_parent(sentinel_add(pool.baseAddr + biti * 16), sentinel_add(pbot));
                    }
                }
            }
        }
        anychanges |= changes;
    }

oldrev 2007-09-11
没仔细研究过它们的GC实现,不过 DM 网站上说的大概原理就是彻底扫描堆栈和寄存器,还真是暴力的说
qiezi 2007-09-14
扫描堆栈是肯定要的,不过扫描的前提是这些指针在堆栈上是总线对齐的.这个还算容易做到,不过tango里面的Fiber又如何处理呢?那可是很多个堆栈..我看它的GC好像也没作特别处理,但这个也不可能在其它地方处理了.
redsea 2007-09-14
qiezi 写道
扫描堆栈是肯定要的,不过扫描的前提是这些指针在堆栈上是总线对齐的.这个还算容易做到,不过tango里面的Fiber又如何处理呢?那可是很多个堆栈..我看它的GC好像也没作特别处理,但这个也不可能在其它地方处理了.


fiber 用的内存如果是从 gc 中申请的, 那不会有问题啊.

从 gc 申请内存的时候, 会打上标志, 这个内存是否需要 scan.

如果你申请的内存是 byte[], 那么不会需要 scan, 如果是 Object[], void[] 之类的, 需要 scan.

在mark 的过程中, 从 root 开始, 只要有一个 pointer(可能并非一个真pointer, 只是对齐的几个字节而已) 指向一个内存块, 这个内存块被 mark 为需要scan, 那么这个内存会被当作所有都是指针, 被 scan.

所以, 这个gc 实现会出现一些内存实际上没有被用, 但是不被释放的 --- 你的其他数据被当作pointer 了, 刚好指向某个 block .

根据这点, 我想, 定义数据结构的时候, 大型的静态数组最好就不要和其他指针, 对象同时放在一个 struct/class 里面, gc 就可以降低一些工作负担.
dayn9 2007-09-14
“其他数据被当作pointer 了”
这种碰撞几率应该极低.
qiezi 2007-09-14
redsea 写道
qiezi 写道
扫描堆栈是肯定要的,不过扫描的前提是这些指针在堆栈上是总线对齐的.这个还算容易做到,不过tango里面的Fiber又如何处理呢?那可是很多个堆栈..我看它的GC好像也没作特别处理,但这个也不可能在其它地方处理了.


fiber 用的内存如果是从 gc 中申请的, 那不会有问题啊.

从 gc 申请内存的时候, 会打上标志, 这个内存是否需要 scan.

如果你申请的内存是 byte[], 那么不会需要 scan, 如果是 Object[], void[] 之类的, 需要 scan.

在mark 的过程中, 从 root 开始, 只要有一个 pointer(可能并非一个真pointer, 只是对齐的几个字节而已) 指向一个内存块, 这个内存块被 mark 为需要scan, 那么这个内存会被当作所有都是指针, 被 scan.

所以, 这个gc 实现会出现一些内存实际上没有被用, 但是不被释放的 --- 你的其他数据被当作pointer 了, 刚好指向某个 block .

根据这点, 我想, 定义数据结构的时候, 大型的静态数组最好就不要和其他指针, 对象同时放在一个 struct/class 里面, gc 就可以降低一些工作负担.

Fiber的的栈可是要切换的,所以说有很多栈.

如果从堆上分配一块内存,把它切换成执行栈,就有另一个栈被切换出去,目前GC只是扫描线程栈而已.

GC只扫描当前线程线和GC里分配的内存,而Fiber的栈上可能有临时分配的对象,GC不会扫描到这部分.
redsea 2007-09-15
dayn9 写道
“其他数据被当作pointer 了”
这种碰撞几率应该极低.


32bit value, 总共的值域有 4G 个不同的值.
如果我的程序用了1G的内存空间, 那么 1/4 的几率冲突.
redsea 2007-09-15
fiber 的内存不是从 gc 申请的吗? 直接 malloc 的 ?
qiezi 写道

Fiber的的栈可是要切换的,所以说有很多栈.

如果从堆上分配一块内存,把它切换成执行栈,就有另一个栈被切换出去,目前GC只是扫描线程栈而已.

GC只扫描当前线程线和GC里分配的内存,而Fiber的栈上可能有临时分配的对象,GC不会扫描到这部分.
oldrev 2007-09-15
http://www.digitalmars.com/d/garbage.html
qiezi 2007-09-16
redsea 写道
fiber 的内存不是从 gc 申请的吗? 直接 malloc 的 ?
qiezi 写道

Fiber的的栈可是要切换的,所以说有很多栈.

如果从堆上分配一块内存,把它切换成执行栈,就有另一个栈被切换出去,目前GC只是扫描线程栈而已.

GC只扫描当前线程线和GC里分配的内存,而Fiber的栈上可能有临时分配的对象,GC不会扫描到这部分.

从GC申请有什么用?关键是GC什么时候回收它,在使用过程中被回收可是很危险的,你必须告诉GC你还在使用这块内存,Fiber的栈对于GC来说是看不见的,所以扫描堆栈根本扫不到这边。

Global site tag (gtag.js) - Google Analytics