Android系统的运行时库层代码是用C++来编写的,用C++来写代码最容易出错的地方就是指针了,一旦使用不当,轻则造成内存泄漏,重则造成系统崩溃。不过系统为我们提供了智能指针,避免出现上述问题,本文将系统地分析Android系统智能指针(轻量级指针、强指针和弱指针)的实现原理。

        在使用C++来编写代码的过程中,指针使用不当造成内存泄漏一般就是因为new了一个对象并且使用完之后,忘记了delete这个对象,而造成系统崩溃一般就是因为一个地方delete了这个对象之后,其它地方还在继续使原来指向这个对象的指针。为了避免出现上述问题,一般的做法就是使用引用计数的方法,每当有一个指针指向了一个new出来的对象时,就对这个对象的引用计数增加1,每当有一个指针不再使用这个对象时,就对这个对象的引用计数减少1,每次减1之后,如果发现引用计数值为0时,那么,就要delete这个对象了,这样就避免了忘记delete对象或者这个对象被delete之后其它地方还在使用的问题了。但是,如何实现这个对象的引用计数呢?肯定不是由开发人员来手动地维护了,要开发人员时刻记住什么时候该对这个对象的引用计数加1,什么时候该对这个对象的引用计数减1,一来是不方便开发,二来是不可靠,一不小心哪里多加了一个1或者多减了一个1,就会造成灾难性的后果。这时候,智能指针就粉墨登场了。首先,智能指针是一个对象,不过这个对象代表的是另外一个真实使用的对象,当智能指针指向实际对象的时候,就是智能指针对象创建的时候,当智能指针不再指向实际对象的时候,就是智能指针对象销毁的时候,我们知道,在C++中,对象的创建和销毁时会分别自动地调用对象的构造函数和析构函数,这样,负责对真实对象的引用计数加1和减1的工作就落实到智能指针对象的构造函数和析构函数的身上了,这也是为什么称这个指针对象为智能指针的原因。

        在计算机科学领域中,提供垃圾收集(Garbage Collection)功能的系统框架,即提供对象托管功能的系统框架,例如Java应用程序框架,也是采用上述的引用计数技术方案来实现的,然而,简单的引用计数技术不能处理系统中对象间循环引用的情况。考虑这样的一个场景,系统中有两个对象A和B,在对象A的内部引用了对象B,而在对象B的内部也引用了对象A。当两个对象A和B都不再使用时,垃圾收集系统会发现无法回收这两个对象的所占据的内存的,因为系统一次只能收集一个对象,而无论系统决定要收回对象A还是要收回对象B时,都会发现这个对象被其它的对象所引用,因而就都回收不了,这样就造成了内存泄漏。这样,就要采取另外的一种引用计数技术了,即对象的引用计数同时存在强引用和弱引用两种计数,例如,Apple公司提出的Cocoa框架,当父对象要引用子对象时,就对子对象使用强引用计数技术,而当子对象要引用父对象时,就对父对象使用弱引用计数技术,而当垃圾收集系统执行对象回收工作时,只要发现对象的强引用计数为0,而不管它的弱引用计数是否为0,都可以回收这个对象,但是,如果我们只对一个对象持有弱引用计数,当我们要使用这个对象时,就不直接使用了,必须要把这个弱引用升级成为强引用时,才能使用这个对象,在转换的过程中,如果对象已经不存在,那么转换就失败了,这时候就说明这个对象已经被销毁了,不能再使用了。

       了解了这些背景知识后,我们就可以进一步学习Android系统的智能指针的实现原理了。Android系统提供了强大的智能指针技术供我们使用,这些智能指针实现方案既包括简单的引用计数技术,也包括了复杂的引用计数技术,即对象既有强引用计数,也有弱引用计数,对应地,这三种智能指针分别就称为轻量级指针(Light Pointer)、强指针(Strong Pointer)和弱指针(Weak Pointer)。无论是轻量级指针,还是强指针和弱指针,它们的实现框架都是一致的,即由对象本身来提供引用计数器,但是它不会去维护这个引用计数器的值,而是由智能指针来维护,就好比是对象提供素材,但是具体怎么去使用这些素材,就交给智能指针来处理了。由于不管是什么类型的对象,它都需要提供引用计数器这个素材,在C++中,我们就可以把这个引用计数器素材定义为一个公共类,这个类只有一个成员变量,那就是引用计数成员变量,其它提供智能指针引用的对象,都必须从这个公共类继承下来,这样,这些不同的对象就天然地提供了引用计数器给智能指针使用了。总的来说就是我们在实现智能指会的过程中,第一是要定义一个负责提供引用计数器的公共类,第二是我们要实现相应的智能指针对象类,后面我们会看到这种方案是怎么样实现的。

        接下来,我们就先介绍轻量级指针的实现原理,然后再接着介绍强指针和弱指针的实现原理。

        1. 轻量级指针

        先来看一下实现引用计数的类LightRefBase,它定义在frameworks/base/include/utils/RefBase.h文件中:

  1. template <class T>   
  2. class LightRefBase   
  3. {   
  4. public:   
  5.     inline LightRefBase() : mCount(0) { }   
  6.     inline void incStrong(const void* id) const {   
  7.         android_atomic_inc(&mCount);   
  8.     }   
  9.     inline void decStrong(const void* id) const {   
  10.         if (android_atomic_dec(&mCount) == 1) {   
  11.             delete static_cast<const T*>(this);   
  12.         }   
  13.     }   
  14.     //! DEBUGGING ONLY: Get current strong ref count.   
  15.     inline int32_t getStrongCount() const {   
  16.         return mCount;   
  17.     }   
  18.    
  19. protected:   
  20.     inline ~LightRefBase() { }   
  21.    
  22. private:   
  23.     mutable volatile int32_t mCount;   
  24. };   

这个类很简单,它只一个成员变量mCount,这就是引用计数器了,它的初始化值为0,另外,这个类还提供两个成员函数incStrong和decStrong来维护引用计数器的值,这两个函数就是提供给智能指针来调用的了,这里要注意的是,在decStrong函数中,如果当前引用计数值为1,那么当减1后就会变成0,于是就会delete这个对象。

     前面说过,要实现自动引用计数,除了要有提供引用计数器的基类外,还需要有智能指针类。在Android系统中,配合LightRefBase引用计数使用的智能指针类便是sp了,它也是定义在frameworks/base/include/utils/RefBase.h文件中:

  1. template <typename T>   
  2. class sp   
  3. {   
  4. public:   
  5.     typedef typename RefBase::weakref_type weakref_type;   
  6.    
  7.     inline sp() : m_ptr(0) { }   
  8.    
  9.     sp(T* other);   
  10.     sp(const sp<T>& other);   
  11.     template<typename U> sp(U* other);   
  12.     template<typename U> sp(const sp<U>& other);   
  13.    
  14.     ~sp();   
  15.    
  16.     // Assignment   
  17.    
  18.     sp& operator = (T* other);   
  19.     sp& operator = (const sp<T>& other);   
  20.    
  21.     template<typename U> sp& operator = (const sp<U>& other);   
  22.     template<typename U> sp& operator = (U* other);   
  23.    
  24.     //! Special optimization for use by ProcessState (and nobody else).   
  25.     void force_set(T* other);   
  26.    
  27.     // Reset   
  28.    
  29.     void clear();   
  30.    
  31.     // Accessors   
  32.    
  33.     inline  T&      operator* () const  { return *m_ptr; }   
  34.     inline  T*      operator-> () const { return m_ptr;  }   
  35.     inline  T*      get() const         { return m_ptr; }   
  36.    
  37.     // Operators   
  38.    
  39.     COMPARE(==)   
  40.         COMPARE(!=)   
  41.         COMPARE(>)   
  42.         COMPARE(<)   
  43.         COMPARE(<=)   
  44.         COMPARE(>=)   
  45.    
  46. private:   
  47.     template<typename Y> friend class sp;   
  48.     template<typename Y> friend class wp;   
  49.    
  50.     // Optimization for wp::promote().   
  51.     sp(T* p, weakref_type* refs);   
  52.    
  53.     T*              m_ptr;   
  54. };   

 

   这个类的内容比较多,但是这里我们只关注它的成员变量m_ptr、构造函数和析构函数。不难看出,成员变量m_ptr就是指向真正的对象了,它是在构造函数里面初始化的。接下来我们就再看一下它的两个构造函数,一个是普通构造函数,一个拷贝构造函数:

  1. template<typename T>   
  2. sp<T>::sp(T* other)   
  3.     : m_ptr(other)   
  4. {   
  5.     if (other) other->incStrong(this);   
  6. }   
  7.    
  8. template<typename T>   
  9. sp<T>::sp(const sp<T>& other)   
  10.     : m_ptr(other.m_ptr)   
  11. {   
  12.     if (m_ptr) m_ptr->incStrong(this);   
  13. }   

    这两个构造函数都会首先初始化成员变量m_ptr,然后再调用m_ptr的incStrong函数来增加对象的引用计数,在我们这个场景中,就是调用LightRefBase类的incStrong函数了。

        最后,看一下析构函数:

  1. template<typename T>   
  2. sp<T>::~sp()   
  3. {   
  4.     if (m_ptr) m_ptr->decStrong(this);   
  5. }   

 

   析构函数也很简单,只是调用m_ptr的成员函数decStrong来减少对象的引用计数值,这里就是调用LightRefBase类的decStrong函数了,前面我们看到,当这个引用计数减1后变成0时,就会自动delete这个对象了。

   轻量级智能指针的实现原理大概就是这样了,比较简单,下面我们再用一个例子来说明它的用法。

        2. 轻量级指针的用法

        参考在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序一文,我们在external目录下建立一个C++工程目录lightpointer,它里面有两个文件,一个lightpointer.cpp文件,另外一个是Android.mk文件。

        源文件lightpointer.cpp的内容如下:

  1. #include <stdio.h>   
  2. #include <utils/RefBase.h>   
  3.    
  4. using namespace android;   
  5.    
  6. class LightClass : public LightRefBase<LightClass>   
  7. {   
  8. public:   
  9.         LightClass()   
  10.         {   
  11.                 printf("Construct LightClass Object.");   
  12.         }   
  13.    
  14.         virtual ~LightClass()   
  15.         {   
  16.                 printf("Destory LightClass Object.");   
  17.         }   
  18. };   
  19.    
  20. int main(int argc, char** argv)   
  21. {   
  22.         LightClass* pLightClass = new LightClass();   
  23.         sp<LightClass> lpOut = pLightClass;   
  24.    
  25.         printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());   
  26.    
  27.         {   
  28.                 sp<LightClass> lpInner = lpOut;   
  29.    
  30.                 printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());   
  31.         }   
  32.    
  33.         printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());   
  34.    
  35.         return 0;   
  36. }   

 

   我们创建一个自己的类LightClass,继承了LightRefBase模板类,这样类LightClass就具有引用计数的功能了。在main函数里面,我们首先new一个LightClass对象,然后把这个对象赋值给智能指针lpOut,这时候通过一个printf语句来将当前对象的引用计数值打印出来,从前面的分析可以看出,如果一切正常的话,这里打印出来的引用计数值为1。接着,我们又在两个大括号里面定义了另外一个智能指针lpInner,它通过lpOut间接地指向了前面我们所创建的对象,这时候再次将当前对象的引用计数值打印出来,从前面 的分析也可以看出,如果一切正常的话,这里打印出来的引用计数值应该为2。程序继承往下执行,当出了大括号的范围的时候,智能指针对象lpInner就被析构了,从前面的分析可以知道,智能指针在析构的时候,会减少当前对象的引用计数值,因此,最后一个printf语句打印出来的引用计数器值应该为1。当main函数执行完毕后,智能指针lpOut也会被析构,被析构时,它会再次减少当前对象的引用计数,这时候,对象的引用计数值就为0了,于是,它就会被delete了。

   编译脚本文件Android.mk的内容如下:

  1. LOCAL_PATH := $(call my-dir)   
  2. include $(CLEAR_VARS)   
  3. LOCAL_MODULE_TAGS := optional   
  4. LOCAL_MODULE := lightpointer   
  5. LOCAL_SRC_FILES := lightpointer.cpp   
  6. LOCAL_SHARED_LIBRARIES := \   
  7.         libcutils \   
  8.         libutils   
  9. include $(BUILD_EXECUTABLE)   

 

 最后,我们参照如何单独编译Android源代码中的模块一文,使用mmm命令对工程进行编译:USER-NAME@MACHINE-NAME:~/Android$ make snod  
  最后得到可执行程序lightpointer就位于设备上的/system/bin/目录下。启动模拟器,通过adb shell命令进入到模拟器终端,进入到/system/bin/目录,执行lightpointer可执行程序,验证程序是否按照我们设计的逻辑运行:

  1. USER-NAME@MACHINE-NAME:~/Android$ adb shell  
  2. root@android:/ # cd system/bin/          
  3. root@android:/system/bin # ./lightpointer                                        
  4. Construct LightClass Object.  
  5. Light Ref Count: 1.  
  6. Light Ref Count: 2.  
  7. Light Ref Count: 1.  
  8. Destory LightClass Object.  

   这里可以看出,程序一切都是按照我们的设计来运行,这也验证了我们上面分析的轻量级智能指针的实现原理。