C++ manages the allocation and release of dynamic memory through new and delete, but developers sometimes forget to use delete. In order to prevent memory leaks and speed up development efficiency, Google created RefBase, SP, wp classes. The paths of these classes are as follows.

  1. system/core/libutils/include/utils/RefBase.h
  2. system/core/libutils/RefBase.cpp
  3. system/core/libutils/include/utils/StrongPointer.h
  4. system/core/libutils/StrongPointer.cpp

Sometimes we need to stand on the shoulders of giants to see further. Therefore, I first looked at deng Fanping teacher wrote << in-depth understanding of Android volume 1>> this book. Therefore, this article also draws on the examples in the book, and the code is as follows

#include <utils/RefBase.h>
#include <utils/StrongPointer.h>

class A : public RefBase {};

int main(a)
{
    A * pa = new A;
    sp<A> spa(pa);
    wp<A> wpa(pa);
	
    // there is no need to use C++ to free memory
    // delet pa;
    return 0;
}
Copy the code

As you can see from this simple example, although new is used to allocate dynamic memory, delete is not used to free dynamic memory because the example does two things

  1. Let class A inherit from the RefBase class.
  2. Dynamic memory is automatically freed when sp or WP objects are destroyed.

Sp: Strong Pointer; wp: Weak Pointer. Java like strong references, weak references.

RefBase

In this example, the A object is created and the RefBase constructor is called

class RefBase
{
// ...

protected:
    RefBase(a);// ...
};
Copy the code

RefBase has only one constructor and protected access, which means that this class can only be used as a base class.

Now look at the implementation of the RefBase constructor

RefBase::RefBase() :mRefs(new weakref_impl(this)) {}Copy the code

The RefBase constructor initializes only its member variable mRefs pointer, which is initialized as a WeakRef_impl object.

class RefBase::weakref_impl : public RefBase::weakref_type
{
// ...

    explicit weakref_impl(RefBase* base): mStrong(INITIAL_STRONG_VALUE) // mStrong indicates the strong reference count. The initial value is1<<28
        , mWeak(0MBase (base) // mBase refers to the RefBase derived class object, mFlags(0) // mFlags represents the life cycle of the RefBase derived class object {
    }

// ...
}    
Copy the code

Weakref_impl is a very critical class, and its member variables have the following meanings

  1. MStrong: It represents the strong reference count, INITIAL_STRONG_VALUE, which is 1<<28.
  2. MWeak: This represents the weak reference count, with an initial value of 0.
  3. MBase: is A pointer to an object derived from RefBase, which is the A object in this example.
  4. MFlags: controls the lifecycle of RefBase derived classes, starting with 0. There are several valid values for mFlags
    enum {
        OBJECT_LIFETIME_STRONG  = 0x0000,
        OBJECT_LIFETIME_WEAK    = 0x0001,
        OBJECT_LIFETIME_MASK    = 0x0001
    };
Copy the code

To summarize, the main purpose of creating a RefBase derived object is to create a WeakRef_impl object that manages strong and weak reference counts and controls the lifecycle.

sp

The constructor

In this example, through SP SPA (PA); Create an sp object with the following constructor

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}
Copy the code

The pointer m_ptr actually points to the RefBase derived object, which in this case is the A object. The constructor calls the incStrong() function of the A object, which is implemented by the RefBase class

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    // The weak reference count is increased by 1
    refs->incWeak(id);
    
    // Non-debug version, add, remove, rename switch functions are empty implementation
    refs->addStrongRef(id);
    
    // Strong reference + 1 and return the old value, in this case INITIAL_STRONG_VALUE
    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);

    // If the old value of the strong reference is not equal to the original value, then the current strong reference count is 1, or 2, or 3, etc
    if(c ! = INITIAL_STRONG_VALUE) {return;
    }

    // Strong reference minus INITIAL_STRONG_VALUE, now 1
    int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
    
    // The callback derives the class object from RefBase
    refs->mBase->onFirstRef(a); }Copy the code

Now we can see that the process of creating an sp object is to increment both strong and weak references by 1. Now both strong and weak references are 1.

The main idea in mind is that sp objects are created primarily to increment strong references by one. Instead, the WP object is created primarily to increment the weak reference count by one. Wp code will not be analyzed later in this article, so keep these two concepts in mind.

The destructor

Now what does the destructor of sp do when the object is destroyed

template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}
Copy the code

From our constructor analysis, the m_ptr pointer actually points to A. The destructor of sp calls the decStrong() function of the A object, which is implemented by RefBase

void RefBase::decStrong(const void* id) const
{
    // Copy the pointer
    weakref_impl* const refs = mRefs;
    
    // Non-debug version, to remove, add, rename switch functions, implementation is empty
    refs->removeStrongRef(id);
    
    // 1. Strong reference minus 1
    // Return the original value of 1, now the strong reference value is 0
    const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);

    // c == 1 indicates that there are no strong references now
    if (c == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        // Call RefBase to notify the derived class object
        refs->mBase->onLastStrongRef(id);
        // mFlags initializes the value 0
        int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
        // 2. Release the dynamic memory of object A under strong life cycle
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {        	
            delete this; }}// 3. Weak reference minus 1 and free yourself
    refs->decWeak(id);
}
Copy the code

The previous analysis indicates that the strong and weak reference counts are now 1 and the mFlags value is OBJECT_LIFETIME_STRONG.

The first step is to reduce the strong reference count by one, so now the strong reference count is 0.

Second, when the strong reference count is 0 and the life cycle is strong, the dynamic memory of object A is freed. So the destructor for class A objects is called, and the destructor for RefBase is automatically called as follows

RefBase::~RefBase()
{
    // get the mFlags value, now 0, which is OBJECT_LIFETIME_STRONG
    int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
    if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
        / ...
    } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
        // ...
    }
    // Reset the mRefs pointer to null
    const_cast<weakref_impl*&>(mRefs) = nullptr;
}
Copy the code

Since the mFlags value is OBJECT_LIFETIME_STRONG, only the mRefs pointer for RefBase is reset to null. MRefs is a pointer to dynamic memory allocated by new, and the memory has not been freed. When I first started analyzing this, I couldn’t understand it. In fact, in the first line of the decStrong() method, the pointer has been copied as follows

weakref_impl* const refs = mRefs;
Copy the code

Therefore, dynamic memory can be freed through refs even if mRefs is reset to null Pointers.

Third, call the decWeak() method of WeakRef_type

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    
    // Non-debug version, functions starting with remove, add, rename, implementation is empty
    impl->removeWeakRef(id);
    
    // 1. The weak reference count is reduced by 1
    // The weak reference count now has a value of 0 and returns the old value of 1
    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);

    if(c ! =1) return;
    atomic_thread_fence(std::memory_order_acquire);

    int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
    if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
    	// The strong reference count is now 0
        if (impl->mStrong.load(std::memory_order_relaxed)
                == INITIAL_STRONG_VALUE) {
            // ...
        } else {
            //2. Delete the dynamic memory pointed to by the mRefs of the original RefBase
            deleteimpl; }}else {
        // ...}}Copy the code

Based on the previous analysis, we now have a strong reference count of 0, a weak reference count of 1, and an mFlag value of OBJECT_LIFETIME_STRONG.

Through the RefBase destructor analysis, we can draw the following conclusions:

The RefBase derived object is freed when the strong reference count is 0 and the object pointed to by the RefBase::mRefs pointer is freed when the weak reference count is 0.

Life cycle control

From the previous analysis, RefBase defaults to strong lifecycle. Under strong life cycles, the dynamic memory of refBase-derived objects is freed only when the strong reference count is zero.

So how do you adjust the RefBase lifecycle to be weak? When will a derived RefBase object be released under a weak lifecycle?

Let’s start with the first question, how to adjust RefBase for weak lifecycle. The following code

class A : public RefBase
{
public:
    A() { extendObjectLifetime(OBJECT_LIFETIME_WEAK); }};Copy the code

When creating the A object, set the weak lifetime by calling extendObjectLifetime(), which sets the mFlags value to OBJECT_LIFETIME_WEAK.

Now to answer the second question, when is the dynamic memory of a RefBase derived object freed under a weak life cycle? Let’s start with the destructor of SP

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
    // c == 1 indicates that there are no strong references now
    if (c == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        refs->mBase->onLastStrongRef(id);
        int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
        // Strong lifecycle operations
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this; }}// Weak references are reduced by 1, and all dynamic memory is released
    refs->decWeak(id);
}
Copy the code

Weakref_type ::decWeak() function weakRef_type ::decWeak() function weakRef_type ::decWeak() function weakRef_type ::decWeak() function weakRef_type ::decWeak() function weakRef_type ::decWeak() function weakRef_type ::decWeak() function weakRef_type

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);
    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);

    if(c ! =1) return;
    atomic_thread_fence(std::memory_order_acquire);

    int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
    if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
        // ...
    } else {        
        impl->mBase->onLastWeakRef(id);
        // This step means that the weak reference count is 0, which frees the dynamic memory of the RefBase derived object
        deleteimpl->mBase; }}Copy the code

As you can see from the code, when the weak reference count is 0, it frees the dynamic memory of the RefBase derived class object, so the destructor of RefBase is called

RefBase::~RefBase()
{
    int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
    if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
        if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
        	// If the weak reference count is 0, the dynamic memory pointed to by mRefs will be freed
            deletemRefs; }}else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
        // ...
    }
    const_cast<weakref_impl*&>(mRefs) = nullptr;
}
Copy the code

The destructor of RefBase frees the dynamic memory pointed to by RefBase::mRefs under a weak lifecycle and with a weak reference count of 0. At this point, all dynamic memory has been successfully freed.

Now to sum up

  1. When a RefBase derived object is in a strong life cycle, the dynamic memory of a RefBase derived object is replenished only when the strong reference count is zero.
  2. When a RefBase derived object is in a weak life cycle, the dynamic memory of a RefBase derived object is replenished only when the weak reference count is 0.

How to use SP, WP

Sp, WP can help us to automatically free dynamic memory, but how to safely use SP, WP to safely operate on RefBase derived objects, so that the RefBase dynamic memory will be freed at some point? This has to find the answer from sp, WP public interface

template<typename T>
class sp {
public:
    // ...
    
    inline T&       operator* () const     { return *m_ptr; }
    inline T*       operator- > ()const    { return m_ptr;  }
    inline T*       get(a) const            { return m_ptr; }
    inline explicit operator bool (a) const { returnm_ptr ! =nullptr; }
    
// ...    
};

template <typename T>
class wp
{
public:
    inline  T* unsafe_get(a) const { return m_ptr; }
    
// ...    };Copy the code

So that’s it. How do you use it

#include <iostream>

class A : public RefBase
{
public:
    void hello(a) const { std::cout << "hello"<< std::endl; }};int main(a)
{
    A * pa = new A(a);sp<A> spa(pa);
    wp<A> wpa(pa);
    
    if (spa)
    {
        // There are three ways to call A's hello() function
        spa->hello(a); (*spa).hello(a); spa.get() - >hello(a); } A * p = wpa.unsafe_get(a);if (p)
    {
        p->hello(a); }return 0;
}
Copy the code

As can be seen, in order to use SP, WP safely, the first need to void. In practice, however, if you are sure that dynamic memory has not been freed, you do not have to nullify it. For example, the sp object has just been created.

LightRefBase

< < understand Android volume 1 > > are a class LightRefBase (path for the system/core/libutils/include/utils/LightRefBase h), it is very similar to RefBase, It’s just that LightRefBase only manages strong reference counts.

So how do you use it? As with RefBase, this class is inherited first

#include <utils/LightRefBase.h>

class A : public LightRefBase 
{
public:
    void hello(a) const;
};
Copy the code

Since only strong reference counting is supported, this class can only be used with SP

sp<A> sp = new A(a); sp->hello(a);Copy the code

idea

Compared with C, C++ sacrifices a bit of runtime efficiency and memory in exchange for development efficiency, and Java is even more so. In a memory-constrained world, the trade-off between memory and runtime efficiency for development efficiency, such as RefBase, should not be acceptable. In a world where memory is not so tight, development efficiency is more important, which is probably why Google designed RefBase classes.