原文地址:http://blog.csdn.net/xuqiqiang1993/article/details/68923160
在Android系统中,Native层的代码基本都是C++写的,C++跟Java不一样,C++没有垃圾回收机制,C++代码中难于管理new出来对象的释放,稍有不慎就造成内存泄漏。针对此问题,Android中提出了一套类似Java垃圾回收机制的智能指针,采用强指针sp(Strong Pointer)和弱指针wp(Weak Pointer)对目标对象进行应用,实现对象的自动回收。下面我们将从C++的基础知识入手,对Android的智能指针展开逐步的分析。
Android的智能指针,巧妙的运用C++的基础特性,实现对象的自动释放,我们就先来看看都用了C++的什么特性。
标记变量的有效范围。从作用域上来看,可以将对象分为全局对象、局部对象、静态全局对象和静态局部对象。
一般来说,局部变量的有效作用域从它的定义点开始,到和定义变量之前最邻近的开括号配对的第一个闭括号,也就是说,作用域由变量所在的最近一对{}括号确定。
[演示代码1]
从内存分配空间来看,可将对象分为栈对象和堆对象。栈对象在作用域结束后会自动释放,而堆对象需要手动显示的释放。
[演示代码2]
图1-1是内存空间的分配示意图。
图 1‑1 内存空间的分配、释放
定义在system/core/libcutils/Atomic.c,依赖于具体的芯片平台。原子操作函数特点:线程安全,返回旧值。
int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) |
加函数,返回旧值,*ptr = *ptr + increment |
int32_t android_atomic_inc(volatile int32_t *addr) |
自增操作,返回旧值,*ptr = *ptr + 1 |
int32_t android_atomic_dec(volatile int32_t *addr) |
自减操作, 返回旧值,*ptr = *ptr - 1 |
int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) |
位与操作,返回旧值,*ptr = *ptr & value |
int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) |
位或操作,返回旧值,*ptr = *ptr | value |
int android_atomic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t *ptr) |
如果*addr == oldvalue,就会执行*addr = new_value的操作,然后返回0,否则返回1 |
表 1‑1 Android原子操作函数一览表
栈对象在生命周期,即作用域结束后自动释放,所以我们这里讨论的是堆对象的引用,也就是指针对象。
图1-2是指针引用时,利用引用数管理实际对象释放的原理图。
引用计数的原理很简单,当引用某个对象时,使其引用数+1;引用结束时,使其引用数-1;当引用数为0时,delete掉实际对象。
根据前面的原理,引出两个问题,带着这两个问题,我们来看看Android是怎么实现的。
Ø 怎么管理引用数?
Ø 怎么判断引用开始和结束,怎么增减引用数?
Android设计了基类RefBase,用以管理引用数,所有类必须从RefBase派生,RefBase是所有对象的始祖。
设计模板类sp、wp,用以引用实际对象,sp强引用和wp弱引用。sp、wp声明为栈对象,作用域结束时,自动释放,自动调用析构函数。因此,可以在sp、wp的构造函数中,增引用数;在析构函数中,减少引用计数。
专门设计weakref_impl类,该类是RefBase的内部类,用来做真正引用数管理,创建实际对象时,同时创建一个mRefs对象。不管是强引用和弱应用,都由mRefs来管理。
图2-1 展示Android智能指针的关系类图。
图 2‑1 Android智能指针关系图
看了智能指针的实现原理,我们来看看具体的实现是什么样的。
根据前面的原理,Android设计了强引用sp和弱引用wp,故实际对象的释放,可分为强引用控制和弱引用控制。所谓强引用控制,指的是强引用数mStrong为0时,释放实际对象;弱引用控制,则指的是弱引用数mWeak为0时,才释放实际对象。
下面将结合一些实例,分析具体的实现。我们先来看一段代码实例。
[代码实例1]
在实例代码中,我们先定义了一个类Sheep,从RefBase派生,创建了一个实际对象,pSheep 指向实际对象。
在构造Sheep的实际对象时,将调RefBase的构造函数。RefBase的构造函数如下,在构造函数中创建mRefs。
weakref_impl从weakref_type派生,mRefs才是真正的“管家”。
[system/core/libutils/RefBase.cpp]
请注意这里的mFlags,默认值为0,可通过修改这个标志来设置是强引用控制,还是弱引用控制,代码如下:
[system/core/include/utils/RefBase.h]
mFlags默认为0,即OBJECT_LIFETIME_STRONG,强引用控制。设置为OBJECT_LIFETIME_WEAK时,为弱引用控制。可以通过extendObjectLifetime函数修改,代码如下:
[system/core/libutils/RefBase.cpp]
接下来,我们创建了一个sp对象spSheep,这是一个栈对象,在其作用域结束后将自动释放,调用sp的析构函数。
[system/core/include/utils/StrongPointer.h]
other指向真正的Sheep对象,在sp的构造函数中,将other赋值给了sp的m_ptr,m-ptr就指向了真正的Sheep对象。
因而,other->incStrong(this),实际就是Sheep的父类RefBase的incStrong函数,代码如下:
[system/core/libutils/RefBase.cpp]
在incStrong函数中调用refs 的incWeak函数,incWeak的代码如下:
[system/core/libutils/RefBase.cpp]
OK,sp构造完成,增加一次强引用。sp构造完成后,mRefs的强引用数变为1,弱引用数也变为1;第一次强引用时,回调onFirstRef()。
接下来,我们创建了一个wp对象wpSheep,这是一个栈对象,在其作用域结束后将自动释放,调用wp的析构函数。
[system/core/include/utils/RefBase.h]
other指向真正的Sheep对象,在wp的构造函数中,将other赋值给了wp的m_ptr,m-ptr就指向了真正的Sheep对象。
因而,other-> createWeak (this),实际就是Sheep的父类RefBase的createWeak函数,代码如下:
[system/core/libutils/RefBase.cpp]
reateWeak时,调用incWeak,最终的影响是弱引用数+1。现在,我们的实例中,强引用数为1,弱引用数为2。
返回值为mRefs,也就是m_refs和mRefs指向同一个weakref_impl对象,而mRefs的mBase指向真正的对象Sheep。因此此处的spSheep和wpSheep都是管理同一个真正的对象。
继续看我们的实例代码,现在wpSheep的作用域结束,将调wp的析构函数,wp析构函数的代码如下:
[system/core/include/utils/RefBase.h]
在wp的析构函数中调用m_refs的decWeak函数。m_refs和mRef指向同一个weakref_impl对象,decWeak代码如下:
[system/core/libutils/RefBase.cpp]
wp析构,情况比较复杂,总的说来做了以下几件事:
Ø 弱引用数减1。
Ø 最后一次弱引用时,强引用控制,释放mRefs,若没有强引用,释放实际对象
Ø 最后一次弱引用时,弱引用控制,释放实际对象
就我们的实例来看,此时,强引用数为1,弱引用数为1,并没有任何释放。
在我们的实例代码中,wp析构完后,sp的作用域也就结束了。此时,会调用sp的析构函数,代码如下:
[system/core/include/utils/StrongPointer.h]
在析构函数中调用m_ptr的decStrong函数,m_ptr指向实际对象。此处为Sheep的父类RefBase的decStrong函数,代码如下:
[system/core/libutils/RefBase.cpp]
refs的decWeak函数,前面wp析构的时候分析过,这里不再重复。sp析构完成,主要完成以下工作:
Ø 强引用数减1,弱引用数据减1。
Ø 最后一次强引用时,若是强引用控制,释放实际对象,释放mRefs,调用onLastStrongRef函数。
在我们的代码中,此时强引用数为0,弱引用数为0,实际对象的析构函数将被调用,mRefs将被释放。下面我看看实际对象的析构函数。
实际对象的析构,先析构RefBase,RefBase的析构函数如下:
[system/core/libutils/RefBase.cpp]
OK,RefBase析构分析完了,在RefBase的析构函数中主要的工作就是释放mRefs指向的weakref_impl的对象。
到此,我们的实例代码分析完成,我们首先构造一个Sheep对象,pSheep指向实际对象。再分别构造一个强引用sp和一个弱引用wp,用以引用实际对象,实际对象的释放就由sp和wp控制,我们并没有显示的释放构造的pSheep指向的实际对象。
我们来看看实例代码1中,对象的构造和析构Log:
[示例代码1的Log]
通过前面的分析,我们可以绘制出实际对象的状态图,如下如所示:
图 3‑1 实际对象的状态图
前面我们通过示例代码1,知道了智能指针是怎么管理实际对象的,怎么控制实际对象的释放的。但是我们只是分析了其中的构造函数和析构函数,下面我们将对智能指针做全面的了解。
我们先看看RefBase的类图,如图4-1所示。
图 4‑1 RefBase类图
Ø 所有类须从RefBase派生,只有一个无参构造函数,RefBase析构函数需申明为virtual。
Ø 在构造函数中创建mRefs对象,为weakref_impl类型。
Ø 可以在派生类中通过函数extendObjectLifetime指定是强引用控制,还是弱引用控制,默认为强引用控制。
Ø 在析构函数中,判断是否释放mRefs。
Ø 私有的构造函数和赋值运算重载,不允许子类使用。
Ø 获取实际对象的强引用数getStrongCount