ivmai / bdwgc

The Boehm-Demers-Weiser conservative C/C++ Garbage Collector (bdwgc, also known as bdw-gc, boehm-gc, libgc)

Home Page:https://www.hboehm.info/gc/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to make this simple circular reference collected?

thautwarm opened this issue · comments

MWE here: https://github.com/thautwarm/dotnet-bdwgc/blob/main/deps/mwe.c

  • OS: Windows 11
  • C Compiler: gcc version 13.2.0 (Rev2, Built by MSYS2 project) Target: x86_64-w64-mingw32
  • bdwgc build options:
   -DENABLE_DISCLAIM
   -DGC_ATOMIC_UNCOLLECTABLE
   -DGC_GCJ_SUPPORT
   -DJAVA_FINALIZATION
   -DNO_EXECUTE_PERMISSION
   -DUSE_MMAP
   -DUSE_MUNMAP
   -DGC_THREADS
   -DPARALLEL_MARK
   -DTHREAD_LOCAL_ALLOC

Basically I want to make the following code collected, but it didn't.

void test()
{
    struct S* s = (struct S*) GC_malloc(sizeof(struct S));
    // EDIT: changing `s->self = NULL`  to `s->self = s` 
    //       OP writes `s->self = NULL` then `s` is not a circular reference
    s->self = s;
    GC_register_finalizer(s, finalizer, NULL, NULL, NULL);
}

int main()
{
    for(int i = 0; i < 10000; i++)
        test();
  
    GC_gcollect();
    printf("done\n");
}

Sorry, I can't reproduce such behavior:

test581.c:

#include "gc.h"

void finalizer(void *obj, void *client_data)
{
}

struct S { void *self; };

void test()
{
    struct S* s = (struct S*) GC_malloc(sizeof(struct S));
    GC_register_finalizer(s, finalizer, NULL, NULL, NULL);
}

int main()
{
    for(int i = 0; i < 10000; i++)
        test();
  
    GC_gcollect();
    GC_gcollect();
    GC_gcollect();
}

set path=C:\msys64\mingw64\bin
gcc -I include -I libatomic_ops/src -DENABLE_DISCLAIM -DGC_ATOMIC_UNCOLLECTABLE -DGC_GCJ_SUPPORT -DJAVA_FINALIZATION -DNO_EXECUTE_PERMISSION -DUSE_MMAP -DUSE_MUNMAP -DGC_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC test581.c extra/gc.c
set GC_PRINT_STATS=1
a.exe

a.gc.log content (tail):

--> Marking for collection #12 after 69280 allocated bytes
World-stopped marking took 0 ms 0 ns (1 ms in average)
GC #12 freed 35520 bytes, heap 128 KiB (+ 0 KiB unmapped + 426 KiB internal)
In-use heap: 0% (84 KiB pointers + 68 KiB other)
1 finalization entries; 0/0 short/long disappearing links alive
1076 finalization-ready objects; 0/0 short/long links cleared
Finalize and initiate sweep took 0 ms 0 ns + 0 ms 0 ns
Complete collection took 16 ms 0 ns
Initiating full world-stop collection!

--> Marking for collection #13 after 0 allocated bytes
World-stopped marking took 15 ms 0 ns (2 ms in average)
GC #13 freed 69632 bytes, heap 128 KiB (+ 0 KiB unmapped + 426 KiB internal)
In-use heap: 12% (17 KiB pointers + 0 KiB other)
1 finalization entries; 0/0 short/long disappearing links alive
0 finalization-ready objects; 0/0 short/long links cleared
Finalize and initiate sweep took 0 ms 0 ns + 0 ms 0 ns
Complete collection took 15 ms 0 ns
Initiating full world-stop collection!

--> Marking for collection #14 after 0 allocated bytes
World-stopped marking took 0 ms 0 ns (2 ms in average)
GC #14 freed 0 bytes, heap 128 KiB (+ 0 KiB unmapped + 426 KiB internal)
In-use heap: 12% (17 KiB pointers + 0 KiB other)
1 finalization entries; 0/0 short/long disappearing links alive
0 finalization-ready objects; 0/0 short/long links cleared
Finalize and initiate sweep took 0 ms 0 ns + 0 ms 0 ns
Complete collection took 0 ms 0 ns

You cant see that almost all finalizable objects are freed.

Sorry, I made a mistake in the definition of the test function, it should be:

void test()
{
    struct S* s = (struct S*) GC_malloc(sizeof(struct S));
    s->self = s; // this is the key point
    GC_register_finalizer(s, finalizer, NULL, NULL, NULL);
}

I tried changing the build options, then I got a .log file which has a lot of lines similar to the following:

GC Warning: Finalization cycle involving 00000297bcc10420

Does this mean that the circular references are handled, but finalization for circular references are not supported tho?

I finally find the related words from the docs.

If in the process of marking from an object the object itself becomes marked,
we have uncovered a cycle involving the object. This usually results in
a warning from the collector. Such objects are not finalized, since it may be
unsafe to do so. See the more detailed discussion of
finalization semantics.

Yes, use GC_register_finalizer_ignore_self or ..._no_order.