最近使用Cocos2d-x开发游戏,发现Cocos2d-x的内存管理采用Objective-C的机制,大喜过望。因为只要坚持Objective-C的原则“谁创建谁释放,谁备份谁释放”的原则即可确保内存使用不易出现Bug。
但是因为本身开放的游戏需要使用到多线程技术,导致测试的时候总是莫名其妙的导致空指针错误。而且是随机出现,纠结了2天无果后,开始怀疑Cocos2d-X的内存本身管理可能存在问题。怀着这样的想法,
一步一步的调试,发现经常出现指针异常的变量总是在调用autorelease后一会就莫名其妙再使用的时候就抛异常。狠下心,在它的析构函数里面断点+Log输出信息。发现对象被释放了。一时也很迷糊,因为对象只是
autorelease,并没有真正释放,是谁导致它释放的?
然后就去看了CCAutoreleasePool的源码,发现存在Cocos2d-X的内存管理在多线程的情况下存在如下问题
如图:thread 1和thread 2是独立的两个线程,它们之间存在CPU分配的交叉集,我们在time 1的时候push一个autorelease的自动释放池,在该线程的末尾,即time 3的时候pop它。同理在thread 2的线程里面,在time 2的时候push一个自动释放池,在time 4的时候释放它,即Pop.
此时我们假设在thread 2分配得到CPU的时候有一个对象obj自动释放,即obj-autorelease().那么在time 3的时候会发生是么事情呢?
答案很简单,就是obj在time 3的时候就被释放了,而我们期望它在time 4的时候才释放。所以就导致我上面说的,在多线程下面,cocos2d-x的autorelease变量会发生莫名其妙的指针异常。
解决办法:在PoolManager给每个线程根据pthread_t的线程id生成一个CCArray的stack的嵌套管理自动释放池。源码如下
所以我在Push的时候根据当前线程的pthread_t的线程id生成一个CCArray的stack来存储该线程对应的Autoreleasepool的嵌套对象
源码如下
但是因为本身开放的游戏需要使用到多线程技术,导致测试的时候总是莫名其妙的导致空指针错误。而且是随机出现,纠结了2天无果后,开始怀疑Cocos2d-X的内存本身管理可能存在问题。怀着这样的想法,
一步一步的调试,发现经常出现指针异常的变量总是在调用autorelease后一会就莫名其妙再使用的时候就抛异常。狠下心,在它的析构函数里面断点+Log输出信息。发现对象被释放了。一时也很迷糊,因为对象只是
autorelease,并没有真正释放,是谁导致它释放的?
然后就去看了CCAutoreleasePool的源码,发现存在Cocos2d-X的内存管理在多线程的情况下存在如下问题
如图:thread 1和thread 2是独立的两个线程,它们之间存在CPU分配的交叉集,我们在time 1的时候push一个autorelease的自动释放池,在该线程的末尾,即time 3的时候pop它。同理在thread 2的线程里面,在time 2的时候push一个自动释放池,在time 4的时候释放它,即Pop.
此时我们假设在thread 2分配得到CPU的时候有一个对象obj自动释放,即obj-autorelease().那么在time 3的时候会发生是么事情呢?
答案很简单,就是obj在time 3的时候就被释放了,而我们期望它在time 4的时候才释放。所以就导致我上面说的,在多线程下面,cocos2d-x的autorelease变量会发生莫名其妙的指针异常。
解决办法:在PoolManager给每个线程根据pthread_t的线程id生成一个CCArray的stack的嵌套管理自动释放池。源码如下
所以我在Push的时候根据当前线程的pthread_t的线程id生成一个CCArray的stack来存储该线程对应的Autoreleasepool的嵌套对象
源码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
class
CC_DLL CCPoolManager
{
/【diff - begin】- by layne//
//CCArray* m_pReleasePoolStack;
//CCAutoreleasePool* m_pCurReleasePool;
// CCAutoreleasePool* getCurReleasePool();
CCDictionary* m_pReleasePoolMultiStack;
pthread_mutex_t m_mutex;
// for multi-thread lock
CCArray* getCurReleasePoolStack();
CCAutoreleasePool* getCurReleasePool(
bool
autoCreate =
false
);
/【diff - end】- by layne//
public
:
CCPoolManager();
~CCPoolManager();
void
finalize();
void
push();
void
pop();
void
removeObject(CCObject* pObject);
void
addObject(CCObject* pObject);
static
CCPoolManager* sharedPoolManager();
static
void
purgePoolManager();
friend
class
CCAutoreleasePool;
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
|
//--------------------------------------------------------------------
//
// CCPoolManager
//
//--------------------------------------------------------------------
/【diff - begin】- by layne//
CCPoolManager* CCPoolManager::sharedPoolManager()
{
if
(s_pPoolManager ==
NULL
)
{
s_pPoolManager =
new
CCPoolManager();
}
return
s_pPoolManager;
}
void
CCPoolManager::purgePoolManager()
{
CC_SAFE_DELETE(s_pPoolManager);
}
CCPoolManager::CCPoolManager()
{
// m_pReleasePoolStack = new CCArray();
// m_pReleasePoolStack->init();
// m_pCurReleasePool = 0;
m_pReleasePoolMultiStack =
new
CCDictionary();
}
CCPoolManager::~CCPoolManager()
{
// finalize();
// // we only release the last autorelease pool here
// m_pCurReleasePool = 0;
// m_pReleasePoolStack->removeObjectAtIndex(0);
//
// CC_SAFE_DELETE(m_pReleasePoolStack);
finalize();
CC_SAFE_DELETE(m_pReleasePoolMultiStack);
}
void
CCPoolManager::finalize()
{
if
(m_pReleasePoolMultiStack->count() > 0)
{
//CCAutoreleasePool* pReleasePool;
CCObject* pkey =
NULL
;
CCARRAY_FOREACH(m_pReleasePoolMultiStack->allKeys(), pkey)
{
if
(!pkey)
break
;
CCInteger *key = (CCInteger*)pkey;
CCArray *poolStack = (CCArray *)m_pReleasePoolMultiStack->objectForKey(key->getValue());
CCObject* pObj =
NULL
;
CCARRAY_FOREACH(poolStack, pObj)
{
if
(!pObj)
break
;
CCAutoreleasePool* pPool = (CCAutoreleasePool*)pObj;
pPool->clear();
}
}
}
}
void
CCPoolManager::push()
{
// CCAutoreleasePool* pPool = new CCAutoreleasePool(); //ref = 1
// m_pCurReleasePool = pPool;
//
// m_pReleasePoolStack->addObject(pPool); //ref = 2
//
// pPool->release(); //ref = 1
pthread_mutex_lock(&m_mutex);
CCArray* pCurReleasePoolStack = getCurReleasePoolStack();
CCAutoreleasePool* pPool =
new
CCAutoreleasePool();
//ref = 1
pCurReleasePoolStack->addObject(pPool);
//ref = 2
pPool->release();
//ref = 1
pthread_mutex_unlock(&m_mutex);
}
void
CCPoolManager::pop()
{
// if (! m_pCurReleasePool)
// {
// return;
// }
//
// int nCount = m_pReleasePoolStack->count();
//
// m_pCurReleasePool->clear();
//
// if(nCount > 1)
// {
// m_pReleasePoolStack->removeObjectAtIndex(nCount-1);
//
// // if(nCount > 1)
// // {
// // m_pCurReleasePool = m_pReleasePoolStack->objectAtIndex(nCount - 2);
// // return;
// // }
// m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);
// }
//
// /*m_pCurReleasePool = NULL;*/
pthread_mutex_lock(&m_mutex);
CCArray* pCurReleasePoolStack = getCurReleasePoolStack();
CCAutoreleasePool* pCurReleasePool = getCurReleasePool();
if
(pCurReleasePoolStack && pCurReleasePool)
{
int
nCount = pCurReleasePoolStack->count();
pCurReleasePool->clear();
if
(nCount > 1)
{
pCurReleasePoolStack->removeObject(pCurReleasePool);
}
}
pthread_mutex_unlock(&m_mutex);
}
void
CCPoolManager::removeObject(CCObject* pObject)
{
// CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
//
// m_pCurReleasePool->removeObject(pObject);
pthread_mutex_lock(&m_mutex);
CCAutoreleasePool* pCurReleasePool = getCurReleasePool();
CCAssert(pCurReleasePool,
"current auto release pool should not be null"
);
pCurReleasePool->removeObject(pObject);
pthread_mutex_unlock(&m_mutex);
}
void
CCPoolManager::addObject(CCObject* pObject)
{
// getCurReleasePool()->addObject(pObject);
pthread_mutex_lock(&m_mutex);
CCAutoreleasePool* pCurReleasePool = getCurReleasePool(
true
);
CCAssert(pCurReleasePool,
"current auto release pool should not be null"
);
pCurReleasePool->addObject(pObject);
pthread_mutex_unlock(&m_mutex);
}
CCArray* CCPoolManager::getCurReleasePoolStack()
{
CCArray* pPoolStack =
NULL
;
pthread_t tid = pthread_self();
if
(m_pReleasePoolMultiStack->count() > 0)
{
pPoolStack = (CCArray*)m_pReleasePoolMultiStack->objectForKey((
int
)tid);
}
if
(!pPoolStack) {
pPoolStack =
new
CCArray();
m_pReleasePoolMultiStack->setObject(pPoolStack, (
int
)tid);
pPoolStack->release();
}
return
pPoolStack;
}
CCAutoreleasePool* CCPoolManager::getCurReleasePool(
bool
autoCreate)
{
// if(!m_pCurReleasePool)
// {
// push();
// }
//
// CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
//
// return m_pCurReleasePool;
CCAutoreleasePool* pReleasePool =
NULL
;
CCArray* pPoolStack = getCurReleasePoolStack();
if
(pPoolStack->count() > 0)
{
pReleasePool = (CCAutoreleasePool*)pPoolStack->lastObject();
}
if
(!pReleasePool && autoCreate) {
CCAutoreleasePool* pPool =
new
CCAutoreleasePool();
//ref = 1
pPoolStack->addObject(pPool);
//ref = 2
pPool->release();
//ref = 1
pReleasePool = pPool;
}
return
pReleasePool;
}
/【diff - end】- by layne//
|