I can completely understand the logic behind not freeing the objects with unclear ordering. But I expected that there will be some kind of upper limit / workaround for these kind of objects. But this means that using SetFinalizer on a big object, carelessly, can potentially lead to memory leaks.
I guess if we provide a destructor( like file.Close() or something like that ) for those objects, which just breaks the cycle, it will probably be enough to force the GC to clear the memory. On Tuesday, November 7, 2023 at 8:21:58 AM UTC+5:30 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/6dda0d21-bd9f-45eb-bade-f20ac0996f45n%40googlegroups.com.