Android系统的智能指针轻量级指针强指针和弱指针的实现原理分析.docx
《Android系统的智能指针轻量级指针强指针和弱指针的实现原理分析.docx》由会员分享,可在线阅读,更多相关《Android系统的智能指针轻量级指针强指针和弱指针的实现原理分析.docx(55页珍藏版)》请在冰点文库上搜索。
Android系统的智能指针轻量级指针强指针和弱指针的实现原理分析
Android系统的运行时库层代码是用C++来编写的,用C++来写代码最容易出错的地方就是指针了,一旦使用不当,轻则造成内存泄漏,重则造成系统崩溃。
不过系统为我们提供了智能指针,避免出现上述问题,本文将系统地分析Android系统智能指针(轻量级指针、强指针和弱指针)的实现原理。
在使用C++来编写代码的过程中,指针使用不当造成内存泄漏一般就是因为new了一个对象并且使用完之后,忘记了delete这个对象,而造成系统崩溃一般就是因为一个地方delete了这个对象之后,其它地方还在继续使原来指向这个对象的指针。
为了避免出现上述问题,一般的做法就是使用引用计数的方法,每当有一个指针指向了一个new出来的对象时,就对这个对象的引用计数增加1,每当有一个指针不再使用这个对象时,就对这个对象的引用计数减少1,每次减1之后,如果发现引用计数值为0时,那么,就要delete这个对象了,这样就避免了忘记delete对象或者这个对象被delete之后其它地方还在使用的问题了。
但是,如何实现这个对象的引用计数呢?
肯定不是由开发人员来手动地维护了,要开发人员时刻记住什么时候该对这个对象的引用计数加1,什么时候该对这个对象的引用计数减1,一来是不方便开发,二来是不可靠,一不小心哪里多加了一个1或者多减了一个1,就会造成灾难性的后果。
这时候,智能指针就粉墨登场了。
首先,智能指针是一个对象,不过这个对象代表的是另外一个真实使用的对象,当智能指针指向实际对象的时候,就是智能指针对象创建的时候,当智能指针不再指向实际对象的时候,就是智能指针对象销毁的时候,我们知道,在C++中,对象的创建和销毁时会分别自动地调用对象的构造函数和析构函数,这样,负责对真实对象的引用计数加1和减1的工作就落实到智能指针对象的构造函数和析构函数的身上了,这也是为什么称这个指针对象为智能指针的原因。
在计算机科学领域中,提供垃圾收集(GarbageCollection)功能的系统框架,即提供对象托管功能的系统框架,例如Java应用程序框架,也是采用上述的引用计数技术方案来实现的,然而,简单的引用计数技术不能处理系统中对象间循环引用的情况。
考虑这样的一个场景,系统中有两个对象A和B,在对象A的内部引用了对象B,而在对象B的内部也引用了对象A。
当两个对象A和B都不再使用时,垃圾收集系统会发现无法回收这两个对象的所占据的内存的,因为系统一次只能收集一个对象,而无论系统决定要收回对象A还是要收回对象B时,都会发现这个对象被其它的对象所引用,因而就都回收不了,这样就造成了内存泄漏。
这样,就要采取另外的一种引用计数技术了,即对象的引用计数同时存在强引用和弱引用两种计数,例如,Apple公司提出的Cocoa框架,当父对象要引用子对象时,就对子对象使用强引用计数技术,而当子对象要引用父对象时,就对父对象使用弱引用计数技术,而当垃圾收集系统执行对象回收工作时,只要发现对象的强引用计数为0,而不管它的弱引用计数是否为0,都可以回收这个对象,但是,如果我们只对一个对象持有弱引用计数,当我们要使用这个对象时,就不直接使用了,必须要把这个弱引用升级成为强引用时,才能使用这个对象,在转换的过程中,如果对象已经不存在,那么转换就失败了,这时候就说明这个对象已经被销毁了,不能再使用了。
了解了这些背景知识后,我们就可以进一步学习Android系统的智能指针的实现原理了。
Android系统提供了强大的智能指针技术供我们使用,这些智能指针实现方案既包括简单的引用计数技术,也包括了复杂的引用计数技术,即对象既有强引用计数,也有弱引用计数,对应地,这三种智能指针分别就称为轻量级指针(LightPointer)、强指针(StrongPointer)和弱指针(WeakPointer)。
无论是轻量级指针,还是强指针和弱指针,它们的实现框架都是一致的,即由对象本身来提供引用计数器,但是它不会去维护这个引用计数器的值,而是由智能指针来维护,就好比是对象提供素材,但是具体怎么去使用这些素材,就交给智能指针来处理了。
由于不管是什么类型的对象,它都需要提供引用计数器这个素材,在C++中,我们就可以把这个引用计数器素材定义为一个公共类,这个类只有一个成员变量,那就是引用计数成员变量,其它提供智能指针引用的对象,都必须从这个公共类继承下来,这样,这些不同的对象就天然地提供了引用计数器给智能指针使用了。
总的来说就是我们在实现智能指会的过程中,第一是要定义一个负责提供引用计数器的公共类,第二是我们要实现相应的智能指针对象类,后面我们会看到这种方案是怎么样实现的。
接下来,我们就先介绍轻量级指针的实现原理,然后再接着介绍强指针和弱指针的实现原理。
1.轻量级指针
先来看一下实现引用计数的类LightRefBase,它定义在frameworks/base/include/utils/RefBase.h文件中:
viewplain
1.template
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(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文件中:
viewplain
1.template
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& other);
11. template sp(U* other);
12. template sp(const sp& other);
13.
14. ~sp();
15.
16. // Assignment
17.
18. sp& operator = (T* other);
19. sp& operator = (const sp& other);
20.
21. template sp& operator = (const sp& other);
22. template 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 friend class sp;
48. template 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就是指向真正的对象了,它是在构造函数里面初始化的。
接下来我们就再看一下它的两个构造函数,一个是普通构造函数,一个拷贝构造函数:
viewplain
1.template
2.sp:
:
sp(T* other)
3. :
m_ptr(other)
4.{
5. if (other) other->incStrong(this);
6.}
7.
8.template
9.sp:
:
sp(const sp& other)
10. :
m_ptr(other.m_ptr)
11.{
12. if (m_ptr) m_ptr->incStrong(this);
13.}
这两个构造函数都会首先初始化成员变量m_ptr,然后再调用m_ptr的incStrong函数来增加对象的引用计数,在我们这个场景中,就是调用LightRefBase类的incStrong函数了。
最后,看一下析构函数:
viewplain
1.template
2.sp:
:
~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的内容如下:
viewplain
1.#include
2.#include
3.
4.using namespace android;
5.
6.class LightClass :
public LightRefBase
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 lpOut = pLightClass;
24.
25. printf("Light Ref Count:
%d.\n", pLightClass->getStrongCount());
26.
27. {
28. sp 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的内容如下:
viewplain
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命令对工程进行编译:
viewplain
1.USER-NAME@MACHINE-NAME:
~/Android$ mmm ./external/lightpointer
编译之后,就可以打包了:
viewplain
1.USER-NAME@MACHINE-NAME:
~/Android$ make snod
最后得到可执行程序lightpointer就位于设备上的/system/bin/目录下。
启动模拟器,通过adbshell命令进入到模拟器终端,进入到/system/bin/目录,执行lightpointer可执行程序,验证程序是否按照我们设计的逻辑运行:
viewplain
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.
这里可以看出,程序一切都是按照我们的设计来运行,这也验证了我们上面分析的轻量级智能指针的实现原理。
3.强指针
强指针所使用的引用计数类为RefBase,它LightRefBase类要复杂多了,所以才称后者为轻量级的引用计数基类吧。
我们先来看看RefBase类的实现,它定义在frameworks/base/include/utils/RefBase.h文件中:
viewplain
1.class RefBase
2.{
3.public:
4. void incStrong(const void* id) const;
5. void decStrong(const void* id) const;
6.
7. void forceIncStrong(const void* id) const;
8.
9. //!
DEBUGGING ONLY:
Get current strong ref count.
10. int32_t getStrongCount() const;
11.
12. class weakref_type
13. {
14. public:
15. RefBase* refBase() const;
16.
17. void incWeak(const void* id);
18. void decWeak(const void* id);
19.
20. bool attemptIncStrong(const void* id);
21.
22. //!
This is only safe if you have set OBJECT_LIFETIME_FOREVER.
23. bool attemptIncWeak(const void* id);
24.
25. //!
DEBUGGING ONLY:
Get current weak ref count.
26. int32_t getWeakCount() const;
27.
28. //!
DEBUGGING ONLY:
Print references held on object.
29. void printRefs() const;
30.
31. //!
DEBUGGING ONLY:
Enable tracking for this object.
32. // enable -- enable/disable tracking
33. // retain -- when tracking is enable, if true, then we save a stack trace
34. // for each reference and dereference; when retain == false, we
35. //