Boost logo

Boost :

From: ÒÝÁØ Ñî (yyl_20050115_at_[hidden])
Date: 2020-07-31 10:06:04


Hi Andrey,
Thanks for repaid response.

It is freeing the memory at last decrement, but not immediately.

It frees memory when all related objects¡¯ reference count are decreased to zero.

Since it¡¯s C++, GC is not the best option all the time, It¡¯s not preferable anyway.

The idea is just delay freeing, after all related objects¡¯ refence count is decreased to zero,
and free them all.

    void Dispose() {
        if (this->_ptr != nullptr) {
            PTR* _ptr = this->_ptr;
            this->_ptr = nullptr;
            _ptr->~PTR();
            RelRef(_ptr);
        }
}
As above, dispose method calls its pointing object¡¯s destructor, but not free that object with free(ptr) function,
This gives opportunity to that object to call other shared_ptr to call their objects¡¯ destructors, including self.
And everything a shared_ptr¡¯s destructor does it just decreasing the count, including self-referenced or circular reference.

When every count is decreased correctly, we free them all.

If you have Visual Studio and run the code which detects memory leak with
#ifdef _WIN32
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

You will see no memory leak at all.

As I said, It¡¯s no easy to explain how it works, but the code speaks for itself.


About GC: the same method can be used in other languages which heavily depends on GC,
since we don¡¯t have to trace the object linkage among large objects.

Please run the code. I don¡¯t know how to detect memory leak on *nix, but simply watch how many
times the free(ptr) is called would explain it.

void rcgc_base::Collect(std::vector<void*>& p_wilds)
{
    if (p_wilds.size() > 0) {
        for (auto p = p_wilds.begin(); p != p_wilds.end(); ++p) {
            if (*p) {
                //delete p->first;
                //NOTICE: use free instead of delete to avoid double calling destructor
                free(*p);
            }
        }
        p_wilds.clear();
    }
}

BTW, Collect is set to called directly after Dispose, which means, when SetAutoCollect(true),
There is only necessary delay for freeing a shared_ptr managed object.


Please let me know if I¡¯m clear on this explaination.

Sincerely,
Yilin




·¢ËÍ×Ô Windows 10 °æÓʼþ<https://go.microsoft.com/fwlink/?LinkId=550986>Ó¦ÓÃ

·¢¼þÈË: Andrey Semashev via Boost<mailto:boost_at_[hidden]>
·¢ËÍʱ¼ä: 2020Äê7ÔÂ31ÈÕ 17:51
ÊÕ¼þÈË: boost_at_[hidden]<mailto:boost_at_[hidden]>
³­ËÍ: Andrey Semashev<mailto:andrey.semashev_at_[hidden]>
Ö÷Ìâ: Re: [boost] Hello everybody, I have something to share about the shared_ptr class

On 2020-07-31 11:52, ÒÝÁØ Ñî via Boost wrote:
> Hello dear boost members,
>
> I¡¯m quite honored to join this mail list for the first time!
>
> As I was told that the boost project is where the shared_ptr class originated, so I think
> it should be OK to talk about it here.
>
> Here I have some ideas and code on my version of shared_ptr class.
>
> The aim is to remove the usage of weak_ptr and fix the circular reference problem.
>
> Please check code on
> https://github.com/yyl-20020115/RCGC
>
> That¡¯s the idea and primitive implementation.
> I don¡¯t know exactly how to test it more widely among situations, please help me to find
> the very situations that this solution would fail, and I¡¯ll try to fix it ASAP.
>
> READM.md is a good start and README_ZHCN.md is better if you can read Chinese.
> However, I don¡¯t think I can illustrate the algorithm very well both in English and Chinese.
> Well the code speaks for itself, and please read the code and run the program.
>
> Please generate a makefile and build with CMakeLists.txt on *nix platforms if Visual Studio 2019
> (where I code the project) is not available.
>
> All ideas and discussions and help from you are all very welcome!

So, it looks like you're just doing garbage collection in a separate
thread instead of freeing memory on the last reference counter
decrement. Which means that your claim that memory consumption is the
same as the original shared_ptr is not quite true. IMHO, GC is a too
costly and unpredictable solution. I don't see myself ever wanting to
have a GC, unless in a *very* localized and controlled context, like a
single function cleaning up after itself.

Regarding how circular dependencies are resolved, I don't quite see the
solution. In the rcgc_shared_ptr::Dispose method, you immediately
destroy the referenced object, not even when the reference count drops
to zero, which is plain wrong because other pointers may still refer to
the object. If you didn't destroy it, in the circular dependencies case
the reference count would never drop to zero, which means the GC thread
would never collect the object.

Lastly, I question the usefulness of a shared_ptr without a weak_ptr.
Note that weak_ptr is not limited to just circular references
resolution, it has other use cases. Although shared_ptr has other useful
features, like pointer aliasing and deleter erasure, I find myself using
shared_ptr almost exclusively when I also need weak_ptr. When I don't
need weak_ptr I would use intrusive_ptr. But then your rcgc_shared_ptr
doesn't provide any of the additional features of shared_ptr, so there's
even less reason to use it.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk