大家好,今天来为大家解答深入解析Android中的指针管理机制这个问题的一些问题点,包括也一样很多人还不知道,因此呢,今天就来为大家分析分析,现在让我们一起来看看吧!如果解决了您的问题,还望您关注下本站哦,谢谢~
2、对象new后,没有及时删除。
3.野指针。指针指向的对象已被删除,但指针尚未清空。当多个指针同时指向同一个对象,并且其中一个指针释放了该对象时,经常会出现这种情况。
为了解决上述问题,Android引入了引用计数技术。只有当引用计数为0时,对象才被允许并自动释放。然而,传统的引用计数技术在处理父子循环依赖时就显得无能为力了。因此衍生出了强引用计数技术和弱引用计数技术。因为强弱引用计数实际上也包括普通的引用计数,所以我们直接介绍强弱引用计数的实现。
强引用计数和弱引用计数
引用计数需要解决三个关键点:
1.如何知道有新的引用指向该对象,如何知道指向该对象的旧引用已经过期。
2、如何维护对象的引用计数值、存放在哪里、何时被修改。
3. 什么时候释放对象合适?
Android中强引用计数和弱引用计数的整体思路比较简单。
1.为所有需要使用引用计数的对象提供一个基类。这个基类中有一个内部类,负责维护对象的强引用计数和弱引用计数。
2.定义一个强指针类sp并重载其所有与赋值相关的操作。当将一个对象分配给sp时,sp会增加该对象的引用计数。当sp 不再指向该对象时,会在sp 构造函数中解析该对象的引用计数
3、定义弱指针类wp,与sp类似。唯一的区别是没有重载取消引用操作。因此,当wp需要实际使用所指向的对象时,需要将其提升为强指针。
4、对象的生命周期管理方式主要有两种。一是生命周期由强指针控制,当强引用计数达到0时对象被回收。弱指针无法晋升为强指针。一是通过弱指针来控制生命周期。只有当强引用计数和弱引用计数都为0时,对象才能被回收。
以上4点主要思路如下,剩下的就是具体的实施方法以及某些特殊情况的处理。例如,没有强指针曾经指向过该对象,生命周期由强指针控制等等。
举个栗子
我们举一个不恰当的生活例子来比喻:
A是一家烧饼店的老板,会做烧饼并出售。
为了扩大销售,A允许他人签订代理经销合同。它还允许其他人只签署意向合同,然后再签署正式的代理合同。但在签订代理合同之前必须先签订意向合同。
以上是约束条件。
此时,B与A签订了代理合同,并放置在A处。C也与A签订了代理合同,并将其放置在A处。所以A知道他有两个销售代理,并且当他们存在时,他不能关门,因为他们可能随时需要提货。
此时,D也想成为A的经纪人,但拿不定主意,于是他与A签订了一份意向合同,表示自己有这个想法。如果他决定了,他就会签订正式的代理合同。
此时,A手上有三份意向合同和两份代理合同。
假设B、C决定不再担任A的代理人,并解除与A的代理合同和意向合同,此时A仅与D有意向合同。
这时候又分为几种情况。
A 决定关闭该公司。换句话说,A并没有关门。它只看自己手里的代理合同,不看意向合同。而当D想要来正式签订代理合同时,却发现A已经倒闭,代理合同签订失败。
这是一个生命周期由强引用计数控制的例子,强引用计数为0,弱指针无法提升为强指针。
A并没有关店,而是等D来了取消预定的合同后才关店。
这是生命周期受弱引用计数控制,弱引用计数变为0的情况。
而如果A不关门,就得等D来签正式代理合同后才可以。
那么这就是生命周期由弱引用计数控制的情况,弱指针成功晋升为强指针。
从这个例子中,我们可以看到一个问题:如果A手里的意向合同有联系方式,他是否可以直接打电话给D,告诉他他打算关门,而不用等D来签合同?即RefBase除了保存引用计数值之外,是否还可以保存一个wp和sp列表呢?
1、引用产生和失效的判断
关于第一点,Android中定义了两个类模板类sp和wp。它们分别是strongPointer和weakPointer的缩写。它们内部都持有T* m_ptr 的对象引用。所有与赋值和构造相关的函数都被重载。也就是说,当调用诸如spsp_object=object; 时发生时,sp类的重载函数将被调用。这些重载函数内部的主要逻辑是增加object对象的强引用计数值(增加强引用计数的同时,弱引用计数也会同时增加)。 wp也是如此,只是弱引用计数值增加了。因此,通过sp和wp引用一个对象,解决了如何知道新引用的问题。同理,引用失败的时机是在sp和wp的析构函数中,其主要逻辑是减少对象的强弱引用计数。
所以我们这里明确一下,要使用强弱引用技术,必须使用sp和wp来引用一个对象。普通指针没有这个作用。 sp和wp的代码如下:
sp
模板
类sp
{
公共:
typedef 类型名RefBase:weakref_type 弱引用类型; //暂时可以忽略
内联sp():m_ptr(0) {} //构造函数
/*以下段落是与赋值相关的运算符重载和与构造相关的重载函数。当分配或构造sp 时将调用这些函数。除了各个函数原有的逻辑外,最重要的逻辑就是修改被引用对象的引用计数*/
sp(T* 其他);
sp(const sp 其他);
模板sp(U*其他);
templatesp(const sp 其他);
sp 运算符=(T* 其他);
sp 运算符=(const sp other);
templatesp 运算符=(const sp other);
templatesp 运算符=(U* 其他);
/*多于*/
sp; //析构函数减少被引用对象的引用计数
//解引用部分,即调用sp-和sp时。操作,实际上调用的是对其指向的对象m_ptr的操作。
内联T 运算符* () const {return *m_ptr;}
内联T* 运算符-() const {return m_ptr;}
内联T* get() const {return m_ptr;}
.
//其余的比较运算符都是重载的,因为sp的比较实际上应该是m_ptr之间的比较
.
私人的:
sp(T* p,weakref_type* refs); //暂时可以忽略
T* m_ptr; //指向的真实对象。
}
由于C++提供了运算符重载,sp重载了-、*等运算符,使得对sp的操作等同于直接对m_ptr的操作。同时,赋值运算符=被重载,使我们有机会在赋值时执行一些操作。构造函数和复制构造函数也得到适当处理。
wp
wp的主要实现与sp一致,重载比较运算符、解引用运算符、赋值运算符等。在构造函数、复制函数、赋值时递增被引用对象的引用计数。递减析构函数中引用对象的引用计数。其主要代码如下:
模板
类wp
{
公共:
typedef 类型名RefBase:weakref_type 弱引用类型;
内联wp(): m_ptr(0) {}
wp(T* 其他);
wp(const wp 其他);
wp(const sp 其他);
templatewp(U* 其他);
templatewp(const sp other);
templatewp(const wp other);
wp 运算符=(T* 其他);
wp 运算符=(const wp other);
wp 运算符=(const sp other);
templatewp 运算符=(U* 其他);
templatewp 运算符=(const wp other);
templatewpoperator=(const sp other);
wp();
sppromote() 常量; //用于将弱引用提升为强引用
//剩余运算符重载部分
.
私人:
T* m_ptr; //引用对象
弱引用类型*m_refs;
}
可见wp和sp的主要结构是相同的。但wp有一个额外的promote函数和m_refs成员变量。前者用于将弱引用提升为强引用。后者稍后会提到。
2、引用计数值的维护
1中,我们知道当sp或wp被赋值或析构时,对象的引用计数值会被修改。但这个引用计数到底放在哪里呢?放在wp或sp中是不合适的,因为可能有多个sp或wp同时指向同一个对象。由于所有的sp和wp都可以获得引用的对象,所以我们可以想到的是,引用的对象本身维护着引用计数。因此,Android为所有需要实现引用计数的对象提供了一个基类。 RefBase.RefBase有一个weakref_impl类型的成员变量mRefs,它又有两个int_32_t类型的成员变量mStrong和mWeak。它们分别负责记录一个对象的强引用计数和弱引用计数。所有对引用计数的操作实际上都是修改这两个变量的值。需要注意的是weakref_impl继承了weakref_type类型。 weakref_type 类型在RefBase 中定义。
RefBase
类参考库
{
公共:
voidincStrong(const void* id) const;
voiddecStrong(const void* id) const;
类弱引用类型
{
公共:
RefBase* refBase() 常量;
voidincWeak(const void* id);
voiddecWeak(const void* id);
bool attemptsIncStrong(const void* id);
}
受保护:
参考库();
虚拟~RefBase();
鸸鹋{
//默认情况下,对象的生命周期由强引用计数控制
OBJECT_LIFETIME_WEAK=0x0001, //此对象的生命周期由弱引用计数控制
OBJECT_LIFETIME_FOREVER=0x0003; //这个对象的生命周期是由用户自己维护的
};
无效extendObjectLifetime(int32_t模式); //修改对象的生命周期控制条件
枚举{
FIRST_INC_STRONG=0x0001;
}
虚拟无效onFirstRef(); //第一次引用指向这个对象。 Andorid系统中很多组件的初始化都放在这个函数中。
虚拟无效onLastStrongRef(const void* id); //strong引用计数变为0。
virtual bool onIncStrongRef(const void* id);
虚拟无效onLastWeakRef(const void* id); //弱引用计数变为0。
私人:
RefBase(const RefBase o);
RefBase 运算符=(const Refbase o);
weakref_impl* constmRefs; //实际维护强引用计数和弱引用计数的对象
}
weakref_impl
类RefBase:weakref_impl : 公共RefBase:weakref_type
{
公共:
易失性int32_tmStrong;
易失性int32_tmWeak;
RefBase* const mBase;
易失性int32_t mFlags;
weakref_impl(RefBase* 基)
:mStrong(INITIAL_STRONG_VALUE)
,m弱(0)
,mBase(碱基)
,mFlgas(0)
{}
//调试相关函数
.
}
通过类图,我们可以更清楚地理解三者之间的关系。
我们正在观察incStrong、decStrong、incWeak 和decWeak 的实现。
incStrong
void RefBase:incStrong(const void * id) const
{
weakref_impl* const refs=mRefs;
refs-incWeak(id); //添加强引用时,必须同时添加弱引用。因此,弱引用计数的值肯定大于强引用计数。当弱引用计数为0时,强引用计数也必须为0。
const int32_t c=android_atomic_inc(refs-mStrong); //c是atomic_inc之前的值
if (c !=INITIAL_STRONG_VALUE) { //解释已经不是第一次了
返回;
}
android_atomic_add(-INITIAL_STRONG_VALUE, refs-mStrong); //如果是第一次,需要减去INITAL_STRONG_VALUE。
const_cast(this)-onFirstRef();
}
给refs-mStrong设置一个初始值INITIAL_STRONG_VALUE的意义是为了区分这个值是否被修改过。而如果初始值为0,则不清楚它到底是本来就是0,还是经过一系列的增减之后变成了0。这与后续的生命周期有直接关系。如果mStrong的值为INITAL_STRONG_VALUE,则表示没有对该对象的强引用。
incWeak
void RefBase:weakref_type:incWeak(const void* id)
{
weakref_impl* const impl=static_cast(this);
const int32_t c=android_atomic_inc(impl-mWeak);
}
decStrong
void RefBase:decStrong(const void* id) const
{
weakref_impl* const refs=mRefs;
const int32_t c=android_atomic_dec(refs-mStrong);
refs-decWeak(id);
.
}
decWeak
void RefBase:weakref_type:decWeak(const void* id)
{
weakref_impl* const impl=static_cast(this);
const int32_t c=android_atomic_dec(impl-mWeak);
.
}
可以看到,IncStrong、incWeak、decStrong、decWeak的主要逻辑就是修改weakref_impl的成员变量mStrong和mWeak的值。
至此,结合第1节和第2节的内容,我们可以推导出操作强引用计数和弱引用计数的一般步骤。
1.需要使用引用计数的对象必须继承自RefBase。 RefBase中有一个mRefs成员,其实际类型为weakref_impl,其父类型为weakref_type,其中包含两个成员变量mStrong和mWeak。对RefBase 的所有incStrong、incWeak、decWeak 和decStrong 调用的最终操作都是修改这两个值。
2、要使用引用计数,必须使用sp和wp指向一个继承自RefBase的对象。在sp和wp的初始化操作和析构函数中,会调用RefBase相关的方法来操作引用计数。
简化理解就是:
sp或wp初始化或销毁------获取RefBase类型的成员变量m_ptr------获取RefBase类型的weakref_impl类型的成员变量mRefs------修改mStrong和mStrong的值m弱。
3、对象的生命周期
在前两节中,我们已经了解了对象如何感知指向自身的引用变化以及引用计数值变化的过程。但这个引用计数值如何变化以及它与对象的生命周期有何关系还没有提到。
主要是两点。一是RefBase对象回收的时机,二是weakref_impl对象回收的时机。
在第2 节中,我们提到过。 RefBase的生命周期有3种形式:OBJECT_LIFETIME_STRONG、OBJECT_LIFETIME_WEAK、OBJECT_LIFETIME_FOREVER。分别表示强引用计数控制生命周期、弱引用计数控制生命周期、用户控制其生命周期。
OBJECT_LIFETIME_STRONG
这样。 RefBase对象的生命周期完全由强引用计数控制。所以我们很容易想到,当weakref_imp的mStrong值为0时,RefBase对象就可以被释放了。因此我们可以在decStrong中进行判断。事实上,这也是事实。
void RefBase:decStrong(const void* id) const
{
weakref_impl* const refs=mRefs;
const int32_t c=android_atomic_dec(refs-mStrong);
如果(c==1){
const_cast(this)-onLastStrongRef(id);
//生命周期由强引用计数控制,强引用计数值为0,则直接回收该对象。
if ((refs-mFlagsOBJECT_LIFETIME_WEAK) !=OBJECT_LIFETIME_WEAK) {
删除这个;
}
}
refs-decWeak(id);
}
当RefBase的生命周期控制方式不是OBJECT_LIFETIME_WEAK时,即完全由强引用计数决定时,那么当强引用计数为0时可以直接删除这个。
OBJECT_LIFETIME_WEAK
RefBase的生命周期是否由弱引用计数控制。那么当强引用计数为0时,就不能直接删除该对象了。判断必须延迟到decWeak。什么时候
void RefBase:weakref_type:decWeak(const void* id)
{
weakref_impl* const impl=static_cast(this);
const int32_t c=android_atomic_dec(impl-mWeak);
if (c !=1) 返回; //如果弱引用计数不为0,则直接返回
if ((impl-mFlagsOBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK) {
if (impl-mStrong==INITIAL_STRONG_VALUE)
//弱引用计数为0,强引用计数为初始值,生命周期不受弱引用计数控制
//你需要删除该对象。因为没有强引用指向该对象,所以decStrong 没有机会回收该对象。
删除impl-mBase;
别的
//生命周期是由强引用计数控制的,所以现在弱引用计数为0,强引用计数也必须为0,那么该对象已经在decStrong中被回收了。
//删除weakref_impl对象即可
删除实现;
} else { //生命周期由弱引用计数控制
impl-mBase-onLastWeakRef(id);
//生命周期由弱引用计数控制,弱引用计数为0,则需要回收该对象。
if ((impl-mFlagsOBJECT_LIFETIME_FOREVER) !=OBJECT_LIFETIME_FOREVER) {
删除impl-mBase;
} } } 整理一下,RefBase对象的回收时机有以下几种情况: 1、生命周期由强引用计数控制,有强引用指向过该对象,那么对象在decStrong中发现mStrong为0时回收。 2、生命周期由强引用计数控制,从来没有强引用指向过该对象,有弱引用指向过该对象。那么对象在decWeak中发现mWeak为0时回收。 3、生命周期由弱引用计数控制,mWeak为0时回收该对象。 weakref_impl的回收时机则有两个,一是在RefBase的析构函数中。另一个则是在decWeak中的delete impl处。 如果是由弱引用计数控制RefBase生命周期的话,那么当RefBase对象析构时,那弱引用计数肯定为0,强引用计数也肯定为0,所以在RefBase的析构函数中就可以安全回收weakref_impl对象。 如果是由强引用计数控制RefBase生命周期的话,那么当RefBase对象析构时,强引用计数为0,弱引用计数不一定为0,那么就不能够在RefBase析构函数中去回收weakref_impl。而只能在弱引用计数为0时去进行回收。 因此现在RefBase的生命周期彻底和其内部的weakref_impl的mWeak和mStrong的值,以及wp和sp的构造和析构函数联系在一起了。 简而言之就是: wp以及sp的析构函数会触发所引用的RefBase对象的decStrong或者decWeak函数,从而改变RefBase的weakref_impl类型的成员变量的mWeak以及mStrong值。 从而触发RefBase对象的回收逻辑。弱引用提升为强引用
在上一节中,比较特别的一种情况就是强引用计数为0,弱引用计数不为0的情况,且生命周期由强引用计数控制的情况。在这种情况下,实际上RefBase对象已经被回收了,但是仍然有弱引用指向了这个对象。那么怎么防止弱引用使用这个已经被回收的对象呢? Android中使用的方法是不重载wp类的解引用操作符,operator*以及operator->,使得wp无法直接使用m_ptr对象,必须调用wp的promote方法将wp升级为sp才能够使用。因此wp的promote方法是关键。 template spwp::promote() const { returnsp } template sp::sp(T* p, weakref_type* refs):m_ptr(p && refs->attemptIncStrong(this)) ? p : 0) {} //试图调用弱引用所指向对象的weakref_impl成员的attemptIncStrong方法。 bool RefBase:;weakref_type::attemptIncStrong(const void* id) { incWeak(id); weakref_impl* const impl = static_cast(this); int32_t curCount = impl->mStrong; //如果强引用计数不为0,且不为初始值,那肯定是可以增加强引用计数的。 while (curCount >0 && curCount != INITIAL_STRONG_VALUE) { //自旋 //cmpxchg是个原子操作,全称是compareAndChange,只有mStrong的值为curCount时,才会将其值修改为curCount + 1, 否则修改失败。 //这样子便可以防止其他线程先一步修改了mStrong的值。在ConcurrentHashmap以及轻量级锁中都用到了这个原子操作。 if (android_atomic_cmpxchg(curCount,curCount + 1,&impl->mStrong) == 0) { //修改curCount值与前面while中判断的间隙期间,引用计数值没有变化,那么实际上是可以直接返回true了,不会进入后面的分支条件 break; } //修改curCount值与前面while中判断的间隙期间,引用计数值发生变化了。将curCount更新为mStrong的最新值,再次尝试修改。 curCount = impl->mStrong; } //这里退出循环有两种情况,一种是修改成功break了,那么是不会进入下面的条件的。 //另一个是修改失败了并且获取最新的引用计数值时发现其值为0了。那么会进入下面的分支。 if (curCount<= 0 || curCount == INITIAL_STRONG_VALUE) { //之前强引用计数为0,或者为初始值 bool allow; if (curCount == INITIAL_STRONG_VALUE) { //该对象之前并没有被强引用指向过,那么只要生命周期不由弱引用控制,那么该对象就不可能被回收过。那么就可以提升为强指针 //如果该对象生命周期由弱引用控制,显然该对象也未被回收,那么就看调用该对象的onIncStrongAttempted方法看该对象是否允许被提升为强指针。 //onIncStrongAttempted方法参数为FIRST_INC_STRONG时都是返回true的 allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); } else { //该对象的强引用计数为0,那么只有生命周期由弱引用控制的时候,才可能没有被回收,因为当前正有一个弱引用指向对象。 //那么就调用该对象的onIncStrongAttempted方法看该对象是否允许被提升为强指针。 //onIncStrongAttempted方法参数为FIRST_INC_STRONG时都是返回true的 allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); } if (!allow) { decWeak(id); return false; } curCount = android_atomic_inc(&impl->mStrong); //如果允许的话就增加强引用计数 } //修正mStrong 的值并且调用onFirstRef方法。 if (curCount == INITIAL_STRONG_VALUE) { android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); impl->mBase->onFirstRef(); } return true; } //可以看到当参数为FIRST_INC_STRONG时,是一直返回true的 bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) { return (flags& FIRST_INC_STRONG) ? true : false; } 所以结合这段将弱指针提升为强指针的代码,我们可以看到: attemptIncStrong的主要逻辑就是判断该对象是不是已经回收了,实际上只要是该对象没有被回收,那么都是可以被提升到强指针的。(由于这段代码里onIncStrongAttempted都是返回true,所以可以忽略) 如果强引用计数值大于0且不等于INITIAL_STRONG_VALUE,那么就肯定可以提升为强指针。这也是while那段逻辑所判断的。 如果强引用计数等于0,那么只有生命周期由弱引用计数控制的时候才没有被回收。 如果强引用计数等于INITIAL_STRONG_VALUE,那么也是可以被提升为强指针的。关于深入解析Android中的指针管理机制的内容到此结束,希望对大家有所帮助。
【深入解析Android中的指针管理机制】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
感觉这东西听起来很酷!让我电脑越来越智能吗?
有19位网友表示赞同!
我一直想了解一下安卓系统怎么管理内存,这个话题还挺有吸引力的。
有14位网友表示赞同!
之前没听说过“智能指针”,是啥意思呀?
有20位网友表示赞同!
智能指针是不是跟编程有关啊?我要是有这方面的基础才能理解吧。
有16位网友表示赞同!
如果能让手机流畅运行得更久的话,那应该好好了解一下这个东西。
有8位网友表示赞同!
不知道智能指针能不能改善安卓系统的卡顿问题?
有18位网友表示赞同!
我感觉手机的内存管理越来越重要了, 这个话题很贴合当下吧!
有16位网友表示赞同!
学习点新知识呀!这次刚好可以看看这方面的文章。
有18位网友表示赞同!
看来技术发展真是日新月异,以前连听都没听说过这种东西呢!
有10位网友表示赞同!
安卓系统一直在更新迭代,这个智能指针应该是一个很实用的功能吧!
有6位网友表示赞同!
手机越用越卡是很多用户的痛点,不知道智能指针能解决多少问题。
有13位网友表示赞同!
这听起来像一些高深的科技知识,我很感兴趣哦!
有15位网友表示赞同!
希望以后的安卓手机都能配备好这个功能,提升用户体验吧!
有17位网友表示赞同!
文章讲的是啥?反正我感觉我的手机内存还总是不够用...
有14位网友表示赞同!
期待看到更多关于智能指针的应用案例和实际效果分析!
有9位网友表示赞同!
安卓系统越来越先进了吧!能帮我省手机电池吗?
有6位网友表示赞同!
最近想换个新机,如果这个新技术能让我手机跑得更流畅那我很心动!
有9位网友表示赞同!
我听说人工智能会越来越智能,不会是跟智能指针有什么关系吧?
有9位网友表示赞同!
总觉得科技进步总是让人感觉很奇妙,期待它的发展!
有5位网友表示赞同!