Btw, I don't follow the sentence: "the GC necessarily has to keep referents of the object-to-be-finalized live even if the object isn't referenced anymore"
That is true for objects not in cycles that need to be finalized as well. I'm not sure I follow the reasoning here ... Asking about it in Bard <http://bard.google.com>, it explains: " In Go, objects that are in a cyclic structure and that are marked with a finalizer (with SetFinalizer) don't get garbage collected when there are no more live pointers to the cyclic structure because the garbage collector cannot determine a safe order to run the finalizers. " Which seems to match the Boehm collector <https://www.hboehm.info/gc/finalization.html> explanation, described under "Topologically Ordered Finalization". On Tuesday, November 7, 2023 at 3:51:58 AM UTC+1 Michael Knyszek wrote: > Yes, cycles containing a finalizer aren't guaranteed to be freed. As > others have pointed out, this is documented. SetFinalizer is really > designed for a narrow set of use-cases, so concessions were made for > overall GC performance. This case is one of them. > > IIUC, the core problem is a combination of the fact that the cycle can be > arbitrarily deep and the GC necessarily has to keep referents of the > object-to-be-finalized live even if the object isn't referenced anymore. > The GC must follow the pointers in an object's referents, and eventually it > may come upon the almost-dead object it started from. But at that point it > likely has no knowledge of where it came from. It's just a pointer on a > queue. A GC could be implemented that keeps track of where the pointers it > follows came from, but such an implementation would be substantially less > performant. > > Other GCs make the same choice. See the Boehm collector > <https://www.hboehm.info/gc/finalization.html>, for example. > On Monday, November 6, 2023 at 10:20:39 AM UTC-5 Harish Ganesan wrote: > >> Does this behaviour mean that, those memory will never be freed and keep >> piling on ? That can be disastrous. >> >> On Monday, November 6, 2023 at 3:39:50 PM UTC+5:30 Jan wrote: >> >>> For what it's worth, a bit of "memory management" on structures in many >>> cases is very ok (not sure if in your case). So for your cyclic structure >>> with finalizers, requiring the user of your code to call some "Finalize()" >>> method (some destructor method you define) that manually breaks the cycle, >>> often is an ok solution. Fundamentally, it's the same as requiring someone >>> to call Close() on an opened file (or any other resource, like sockets, db >>> connections, etc). >>> >>> As an anecdote, previously when I was doing C++ I was a big fan of >>> referenced counted smart pointers (`shred_ptr<>`), which gets most of the >>> benefit of GC, but with a much lower cost. They required manually breaking >>> cycles, which I didn't find to be an issue at all in the great majority of >>> the cases. >>> >>> On Monday, November 6, 2023 at 11:01:04 AM UTC+1 Jan wrote: >>> >>>> I was very surprised by this behavior of SetFinalizer: and indeed if >>>> you remove the SetFinalizer one can see that s1 is freed, because the >>>> memory reported by `m.HeapAlloc` goes back down. >>>> >>>> I think you already have the answer: the block that has the cycle (s1 >>>> and s2) have a SetFinalizer set, and it will never run, per documentation >>>> (I had never paid attention to this). >>>> >>>> A suggestion to work around would be to move the stuff that needs a >>>> "Finalizer" to a leaf node, as in: >>>> >>>> https://go.dev/play/p/WMMTdAza6aZ >>>> >>>> But I understand this is not a solution if the finalizer needs to >>>> access the S1 (so, if the finalizer function needs any information that is >>>> not self-contained in `S1Data` in my example). >>>> On Sunday, November 5, 2023 at 4:01:14 PM UTC+1 Soren Yang wrote: >>>> >>>>> As shown in the following code: >>>>> >>>>> cyclic structure with finalizer <https://go.dev/play/p/Fn_h08y-L6b> >>>>> >>>>> The s1 & s2 didn't been free, and finalizer didn't run. But when >>>>> enable the line which have been commented, all run as expected(s1 & s2 >>>>> been >>>>> free)。 >>>>> >>>>> I have seen the comment in runtime.SetFinalizer: If a cyclic >>>>> structure includes a block with a finalizer, that cycle is not guaranteed >>>>> to be garbage collected and the finalizer is not guaranteed to run, >>>>> because >>>>> there is no ordering that respects the dependencies. >>>>> >>>>> But why it haven't been free in first code? >>>>> >>>> -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/2ce38690-5d75-45df-ab7f-0106a686e194n%40googlegroups.com.