码字不易,转载请注明出处喔
https://blog.csdn.net/newchenxf/article/details/121537539
1 结论
可能你打开这篇文章,是在某个加班的深夜,时间真的不多了,很想回家了,所以我不能先来一堆废话。这次先上结论,以供参考,如果没用,赶紧再找下一家吧…
出现SIGSEGV
,是进程执行了一个无效的内存引用
。本人目前遇到两次的崩溃,原因不一,这里列举出来。
原因:
对象在使用时已被释放。
对象没有初始化。
再展开一点说:
对象A用局部变量初始化,分配在栈上,函数结束就提前释放了。赋值给其他对象B,B在其他函数引用A时,出现崩溃。
对象没有被初始化,判空又不为空,使用时崩溃。
解决办法:
使用智能指针,或者A改成全局变量。
保证变量在使用前初始化,例如在构造函数初始化。
好了,接下来,详细讨论一下。
2 案例一 对象在使用时已被释放
2.1 问题背景
写好Android C++代码,运行起来崩溃,日志如下:
11-25 15:07:25.546 24467 24527 E glesdemo: ReadMissingBones, m_BoneInfoMap address 0x7e70ed0f88, size 52
11-25 15:07:25.554 24467 24527 E glesdemo: GetBoneIDMap, m_BoneInfoMap size 0
11-25 15:07:25.654 24718 24718 I crash_dump64: performing dump of process 24467 (target tid = 24527)
11-25 15:07:25.655 24718 24718 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
11-25 15:07:25.655 24718 24718 F DEBUG : Build fingerprint: 'Xiaomi/sagit/sagit:8.0.0/OPR1.170623.027/V10.2.1.0.OCACNFK:user/release-keys'
11-25 15:07:25.655 24718 24718 F DEBUG : Revision: '0'
11-25 15:07:25.655 24718 24718 F DEBUG : ABI: 'arm64'
11-25 15:07:25.655 24718 24718 F DEBUG : pid: 24467, tid: 24527, name: GLThread 6769 >>> com.chenxf.opengles <<<
11-25 15:07:25.655 24718 24718 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x23d70a00000020
11-25 15:07:25.655 24718 24718 F DEBUG : x0 3c23d70a00000020 x1 0000007e70ed0ce0 x2 0000000000000018 x3 3c23d70a00000020
11-25 15:07:25.655 24718 24718 F DEBUG : x4 0000007e72355438 x5 0000007e70ed0f68 x6 0000007e70ed11a0 x7 0000000000000000
11-25 15:07:25.655 24718 24718 F DEBUG : x8 0000007e72355420 x9 3c23d70a00000020 x10 0000007e70ed0e98 x11 0000007e70ed11a0
11-25 15:07:25.655 24718 24718 F DEBUG : x12 0000007e71a44808 x13 0000000000000001 x14 ffffffffffffffff x15 26204f3d44495400
11-25 15:07:25.656 24718 24718 F DEBUG : x16 0000007e72bbbac0 x17 0000007e72b828bc x18 0000000000000001 x19 0000007e7efa5400
11-25 15:07:25.656 24718 24718 F DEBUG : x20 0000007e72b70ee4 x21 000000001384f730 x22 000000001384b8f0 x23 0000007e8f90ab47
11-25 15:07:25.656 24718 24718 F DEBUG : x24 0000000000000008 x25 0000007e7efa5498 x26 0000000000000000 x27 0000000000000000
11-25 15:07:25.656 24718 24718 F DEBUG : x28 0000000000000002 x29 0000007e70ed0c80 x30 0000007e72b79bfc
11-25 15:07:25.656 24718 24718 F DEBUG : sp 0000007e70ed0c70 pc 0000007e72b79bfc pstate 0000000020000000
11-25 15:07:25.661 24718 24718 F DEBUG :
11-25 15:07:25.661 24718 24718 F DEBUG : backtrace:
11-25 15:07:25.661 24718 24718 F DEBUG : #00 pc 0000000000028bfc /data/app/com.chenxf.opengles-bU4sOnN8Yuev-eQs6nf7QA==/base.apk (offset 0x1606000)
11-25 15:07:25.661 24718 24718 F DEBUG : #01 pc 0000000000031780 /data/app/com.chenxf.opengles-bU4sOnN8Yuev-eQs6nf7QA==/base.apk (offset 0x1606000)
11-25 15:07:25.661 24718 24718 F DEBUG : #02 pc 000000000003e1ec /data/app/com.chenxf.opengles-bU4sOnN8Yuev-eQs6nf7QA==/base.apk (offset 0x1606000)
11-25 15:07:25.661 24718 24718 F DEBUG : #03 pc 000000000003e1b8 /data/app/com.chenxf.opengles-bU4sOnN8Yuev-eQs6nf7QA==/base.apk (offset 0x1606000)
11-25 15:07:25.661 24718 24718 F DEBUG : #04 pc 000000000003e16c /data/app/com.chenxf.opengles-bU4sOnN8Yuev-eQs6nf7QA==/base.apk (offset 0x1606000)
11-25 15:07:25.661 24718 24718 F DEBUG : #05 pc 000000000003e110 /data/app/com.chenxf.opengles-bU4sOnN8Yuev-eQs6nf7QA==/base.apk (offset 0x1606000)
一点so的堆栈信息都没有。完全不知道哪一行,这会是什么原因呢?
我先把所依赖的代码,简单列出如下:
第一个类Animator:
class Animator
{
public:
Animator(Animation* animation)
{
m_CurrentAnimation = animation;
}
void CalculateBoneTransform()
{
//省略代码...
std::map<std::string,BoneInfo> boneInfoMap = m_CurrentAnimation->GetBoneIDMap();
DEBUG_LOGCATE();
if (boneInfoMap.find(nodeName) != boneInfoMap.end())
{
DEBUG_LOGCATE();
//省略代码...
}
}
private:
Animation* m_CurrentAnimation;
};
第二个类Animation
class Animation
{
public:
Animation() = default;
Animation(const std::string& animationPath, ModelAnim* model)
{
m_BoneInfoMap = model.GetBoneInfoMap();//getting m_BoneInfoMap from Model class
LOGCATE("ReadMissingBones, m_BoneInfoMap address %p, size %d", &m_BoneInfoMap,m_BoneInfoMap.size());
}
~Animation()
{
}
inline const std::map<std::string,BoneInfo>& GetBoneIDMap()
{
LOGCATE("GetBoneIDMap, m_BoneInfoMap size %d", m_BoneInfoMap.size());
return m_BoneInfoMap;
}
private:
std::map<std::string, BoneInfo> m_BoneInfoMap;
};
第三个类,初始化上面2个类,并使用:
void Model3DAnimSample::Init() {
Animation animation(path + "/model/vampire/dancing_vampire.dae", m_pModel);
m_pAnimator = new Animator(&animation);
//...
}
void Model3DAnimSample::Draw(int screenW, int screenH) {
m_pAnimator->CalculateBoneTransform();
//省略...
}
2.2 原因分析
我可以本地复现,所以加了很多日志来跟踪,请看最上面的日志前两行:
11-25 15:07:25.546 24467 24527 E glesdemo: ReadMissingBones, m_BoneInfoMap address 0x7e70ed0f88, size 52
11-25 15:07:25.554 24467 24527 E glesdemo: GetBoneIDMap, m_BoneInfoMap size 0
这个m_BoneInfoMap,赋值的时候,有52,到被Animator的CalculateBoneTransform函数调用时,读取值,就变成0了,再进一步使用,就崩溃了。
为啥呢?看了半天,m_BoneInfoMap没有被其他任何地方修改过!!!
再来看看堆栈信息:
11-25 15:07:25.655 24718 24718 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x23d70a00000020
这2个字符代表啥意思呢?
SIGSEGV
: 进程执行了一个无效的内存引用。
SEGV_MAPERR
: 表示堆栈映射错误。
所以原因出来了:
在Init函数,animation是局部变量,栈上分配的,再赋值给m_pAnimator。 Init函数结束后,animation就无效了,后面m_pAnimator再使用animation,就是栈上的脏数据,所以出现了SIGSEGV
2.3 解决办法
- 把Animation做成类的全局变量。即
m_pAnimation = new Animation(path + "/model/vampire/dancing_vampire.dae", m_pModel);
m_pAnimator = new Animator(m_pAnimation);
- 用智能指针来创建Animation,即share_ptr。有关智能指针,可以参考:https://blog.csdn.net/newchenxf/article/details/116019535
3 案例二 对象没有初始化
3.1 问题背景
和案例1一样的堆栈
12-27 16:06:13.513 20962 20962 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
12-27 16:06:13.513 20962 20962 F DEBUG : Build fingerprint: 'Xiaomi/sagit/sagit:8.0.0/OPR1.170623.027/V10.2.1.0.OCACNFK:user/release-keys'
12-27 16:06:13.513 20962 20962 F DEBUG : Revision: '0'
12-27 16:06:13.513 20962 20962 F DEBUG : ABI: 'arm64'
12-27 16:06:13.513 20962 20962 F DEBUG : pid: 20871, tid: 20905, name: GLThread 7653 >>> com.chenxf.opengles <<<
12-27 16:06:13.513 20962 20962 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x72006500640069
12-27 16:06:13.513 20962 20962 F DEBUG : x0 0072006500640069 x1 0000000000000302 x2 00000000ffffffff x3 0000007196a37e0c
12-27 16:06:13.513 20962 20962 F DEBUG : x4 003d3d3d3d3d204a x5 0000008000000080 x6 26415e3d44495400 x7 0000000000008036
12-27 16:06:13.513 20962 20962 F DEBUG : x8 0072006500640069 x9 0000000000000001 x10 0000000000000003 x11 00000000000000ff
12-27 16:06:13.514 20962 20962 F DEBUG :
12-27 16:06:13.514 20962 20962 F DEBUG : backtrace:
12-27 16:06:13.514 20962 20962 F DEBUG : #00 pc 0000000000028228 /data/app/com.chenxf.opengles-Xbv907eJgfjlz8Zq9XfKYQ==/base.apk (offset 0x3675000)
3.2 原因分析
没有堆栈,所以也是通过日志,看最后执行到哪里。这里列举一下代码:
class TextSample
{
public:
TextSample();
virtual ~TextSample();
virtual void Init();
virtual void Draw(int screenW, int screenH);
private:
Shader *m_pShader;
};
TextSample::TextSample() {
}
void TextSample::Draw(int screenW, int screenH) {
if(m_pShader == nullptr)
return;
DEBUG_LOGCATE();
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(1.0, 1.0, 1.0, 1.0);
// enable alpha blending
// ------------
DEBUG_LOGCATE();
m_pShader->use();
}
日志崩溃在m_pShader->use()
。
但奇怪的是,函数的最前面,已经判断为空了!if(m_pShader == nullptr) return;
为啥呢?因为
m_pShader定义了,但是没初始化,所以和JAVA不一样的是,对C++来说,只要生成了TextSample对象,则内部的m_pShader指针就不是为空!
3.3 解决办法
解决办法非常简单,在构造函数,保证初始化指针!
TextSample::TextSample() {
m_pShader = nullptr;
}
这也是C++工作和JAVA工作的区别吧!JAVA不用操心指针,某个变量定义好了,不需要初始化也可以,默认为空。