Skip to main content

Android C++ reference counting, part 1

Anyone working on the Android native framework can't get around several utility classes that are used almost everywhere by the native C++ code. sp, or StrongPointer, is one of them. It is vital important to understand how it works so that you can understand the code precisely and write code that is clean and won't leak resource. In this article, we will cover the basic ideas and usage of sp, by examples.

#include <utils/RefBase.h>
#include <utils/Log.h>
#include <cstdlib>
#include <cassert>

using namespace android;

// class Memory subclass RefBase[1] so it can be reference counted
// and be accepted by template class sp<T> [2], where the sp stands for
// strong pointer
// [1]https://android.googlesource.com/platform/frameworks/native/+/jb-mr1-dev/include/utils/RefBase.h
// [2]https://android.googlesource.com/platform/frameworks/native/+/jb-mr1-dev/include/utils/StrongPointer.h

class Memory: public RefBase {
public:
    Memory(int size) : mSize(size), mData(NULL) {
        ALOGD("        Memory constructor %p ",this);
    }

    virtual ~Memory() {
        ALOGD("        Memory destructor %p", this);
        if (mData)  free(mData);
    }

    virtual void onFirstRef() {
        ALOGD("        onFirstRef on %p",this);
        mData = malloc(mSize);
    }

    int size() {return mSize;}
private:
    int mSize;
    void *mData;
};

// used as a marker in the output log
#define L(N)   ALOGD("LINE %d TRIGGER:",N);
// print out the strong counter number of the object
#define C(obj) ALOGD("        Count of %p : %d", (void*)obj, obj->getStrongCount());

int main()
{
    {
        // create a Memory instance and assign it to a raw pointer
        L(1)
        Memory *m1 = new Memory(4);
        // create a strong pointer, using constructor sp(T* other),
        // which will increase m1's reference counter by 1, and
        // call m1::onFirstRef, where you can do the lazy initialzation
        L(2)
        sp<Memory> spm1 = m1;
        C(m1);

        // usually, we will combine previous two steps into one single statement.
        // create another strong pointer, spm2, and initialize it.
        // To get the raw object, use sp<T>::get()
        L(3)
        sp<Memory> spm2 = new Memory(128);
        Memory *m2 = spm2.get();
        // to access the method, use sp as if you are working with raw pointer
        int size = spm2->size();

        // create a 3rd sp,spm3,using constructor sp(const sp<T>& other),
        // which will increase the reference counter pointed by spm1 by 1.
        // now, m1 is pointed by two strong pointers, spm1 and spm3
        L(4)
        sp<Memory> spm3 = spm1;
        C(m1);

        // below are same as L(4), except that the scope of spm4 is within the block
        L(5)
        {
            sp<Memory> spm4 = spm1;
            C(m1);
            // at this point, m1 is pointed by spm1, spm3 and spm4
        }

        // beyond this point, spm4 is destructed and no longer point to m1
        // so, the reference of m1 is still 2, i,e pointed by spm1, spm3
        L(6)
        C(m1);

        // trigger sp& operator = (const sp<T>& other);
        L(7)
        // before the assigment, spm2 pointed to m2 and spm3 pointed to m1
        spm3 = spm2;
        // after the assigment, spm3 will no longer point to m1 but m2.
        // so the reference counter of m1 decrease by 1 and the reference counter
        // of m2 increase by 1.
        // now , m1 is pointed by spm1 m2 is pointed by spm2 and spm3
        C(m1);
        C(m2);

        // spm5 is a reference to spm1, no new strong object is created.
        // so reference count of m1 stays the same
        L(8)
        sp<Memory> &spm5 = spm1;
        C(m1);

        // we can also create a smart pointer pointing to nothing at first
        // and later assign it a value. We can also remove the reference explictly
        // by calling sp::clear()
        L(9)
        sp<Memory> spm6;
        assert(spm6.get() == NULL);
        spm6 = spm1;
        C(m1);
        L(10)
        spm6.clear();
        assert(spm6.get() == NULL);
        C(m1);

    }
    // beyond the close curly, all the smart pointer objects are out of
    // scope, so they will be desctructed and cause the reference counter
    // of its managed object decrease by 1. For example,
    // when both spm1 and spm6 destructs,the reference count of m1 decrease to 0
    // and it will trigger the destructor of m1.
    L(-1)
    return 0;
}

And, below is the output of the program.

LINE 1 TRIGGER:
        Memory constructor 0x558f06b050 
LINE 2 TRIGGER:
        onFirstRef on 0x558f06b050
        Count of 0x558f06b050 : 1
LINE 3 TRIGGER:
        Memory constructor 0x558f06b0c0 
        onFirstRef on 0x558f06b0c0
LINE 4 TRIGGER:
        Count of 0x558f06b050 : 2
LINE 5 TRIGGER:
        Count of 0x558f06b050 : 3
LINE 6 TRIGGER:
        Count of 0x558f06b050 : 2
LINE 7 TRIGGER:
        Count of 0x558f06b050 : 1
        Count of 0x558f06b0c0 : 2
LINE 8 TRIGGER:
        Count of 0x558f06b050 : 1
LINE 9 TRIGGER:
        Count of 0x558f06b050 : 2
LINE 10 TRIGGER:
        Count of 0x558f06b050 : 1
        Memory destructor 0x558f06b0c0
        Memory destructor 0x558f06b050
LINE -1 TRIGGER:

In next article, we will look at the problem of circular reference and how to solve it with weak pointer, or wp, as it is called in Android.

Popular posts from this blog

Android Camera2 API Explained

Compared with the old camera API, the Camera2 API introduced in the L is a lot more complex: more than ten classes are involved, calls (almost always) are asynchronized, plus lots of capture controls and meta data that you feel confused about.

No worries. Let me help you out. Whenever facing a complex system need a little bit effort to understand, I usually turns to the UML class diagram to capture the big picture.

So, here is the class diagram for Camera2 API.




You are encouraged to read this Android document first and then come back to this article, with your questions. I'll expand what is said there, and list the typical steps of using camera2 API. 

1. Start from CameraManager. We use it to iterate all the cameras that are available in the system, each with a designated cameraId. Using the cameraId, we can get the properties of the specified camera device. Those properties are represented by class CameraCharacteristics. Things like "is it front or back camera", "outpu…

Java Collections Framework Cheat Sheet

Java Collections Framework (JCF) implements the Abstract Data Type  for Java platform. Every serious Java programmer should familiar himself on this topic and be able to choose the right class for specific need.  A thorough introduction to JCF is not the target of this small article and to achieve that goal you can start with this excellent tutorial . 

Instead, I'd like to
1) Provide an overview of JCF's classes ,   2) Provide a cheat sheet you can post in your cubicel for daily reference, 3) Underline the relationship between JCF's implementation and the data structure and algorithm you learned in your undergraduate course

With these goals in mind, I came up following diagram - Java Collection Cheat Sheet. You can click it to zoom in. There is no necessity for more explanation once your familiar with UML class diagram and have a basic understanding of common data structures.


Android Security: An Overview Of Application Sandbox

The Problem: Define a policy to control how various clients can access different resources. A solution: Each resource has an owner and belongs to a group.Each client has an owner but can belongs to multiple groups.Each resource has a mode stating the access permissions allowed for its owner, group members and others, respectively. In the context of operating system, or Linux specifically, the resources can be files, sockets, etc; the clients are actually processes; and we have three access permissions:read, write and execute.