清理所拥有的(!)字符串成员时,析构函数偶尔崩溃 我试图追查一个在这个普通C++类的析构函数中偶尔会破坏我的应用程序的bug: class CrashClass { public: CrashClass(double r1, double s1, double r2, double s2, double r3, double s3, string dateTime) : mR1(r1), mS1(s1), mR2(r2), mS2(s2), mR3(r3), mS3(s3), mDateTime(dateTime) { } CrashClass() : mR1(0), mS1(0), mR2(0), mS2(0), mR3(0), mS3(0) { } ~CrashClass() {} string GetDateTime() { return mDateTime; } private: double mR1, mS1, mR2, mS2, mR3, mS3; string mDateTime; }; 一组对象被粘贴在一个标准的C++ >代码>矢量< /代码>中,用于第二类: class MyClass { (...) private: vector<CrashClass> mCrashClassVec; };
旁注:要清除的清理所拥有的(!)字符串成员时,析构函数偶尔崩溃 我试图追查一个在这个普通C++类的析构函数中偶尔会破坏我的应用程序的bug: class CrashClass { public: CrashClass(double r1, double s1, double r2, double s2, double r3, double s3, string dateTime) : mR1(r1), mS1(s1), mR2(r2), mS2(s2), mR3(r3), mS3(s3), mDateTime(dateTime) { } CrashClass() : mR1(0), mS1(0), mR2(0), mS2(0), mR3(0), mS3(0) { } ~CrashClass() {} string GetDateTime() { return mDateTime; } private: double mR1, mS1, mR2, mS2, mR3, mS3; string mDateTime; }; 一组对象被粘贴在一个标准的C++ >代码>矢量< /代码>中,用于第二类: class MyClass { (...) private: vector<CrashClass> mCrashClassVec; };,c++,c++11,memory-management,lldb,C++,C++11,Memory Management,Lldb,旁注:要清除的向量可能没有元素(尚未) 在stacktrace(bt all)中,我可以看到其他线程在其CrashClass向量的副本上执行操作,但从比较堆栈跟踪中的地址可以看出,所有这些实际上都是私有副本(按设计),即线程之间不共享任何数据 自然该错误仅在完全生产模式下发生,即所有试图重现崩溃的尝试 在调试模式下运行 在Lldb(Xcode)地址消毒剂下运行(数小时/一夜) 在Lldb(Xcode)的线程消毒剂下运行(数小时/一夜) 运行该类的精简版本,只保留/复制关键代码 失败且未触发崩
向量
可能没有元素(尚未)
在stacktrace(bt all
)中,我可以看到其他线程在其CrashClass
向量的副本上执行操作,但从比较堆栈跟踪中的地址可以看出,所有这些实际上都是私有副本(按设计),即线程之间不共享任何数据
自然该错误仅在完全生产模式下发生,即所有试图重现崩溃的尝试
- 在调试模式下运行
- 在Lldb(Xcode)地址消毒剂下运行(数小时/一夜)
- 在Lldb(Xcode)的线程消毒剂下运行(数小时/一夜)
- 运行该类的精简版本,只保留/复制关键代码
您可以尝试一些技巧:
- 使用单个线程运行生产版本更长的时间(比如一周或两周),看看它是否崩溃
- 考虑到可能存在内存碎片,请确保不消耗所有可用RAM
- 确保程序运行的时间越长,不会出现内存泄漏或增加内存使用
- 通过添加额外的值来添加一些跟踪,将值设置为析构函数中已知的值(这样,如果执行双重删除,您可以识别该模式)
- 尝试在其他平台和编译器下运行该程序
- 您的编译器或库可能包含错误。请尝试另一个(较新的)版本
- 从原始版本中删除代码,直到不再崩溃。如果您能够始终使用某种程度上损坏内存的序列来获得崩溃,那么效果会更好
- 一旦发生崩溃,请使用完全相同的数据(针对每个线程)运行程序,并查看它是否总是在同一位置崩溃
- 重写或验证应用程序中的任何不安全代码。避免使用casting、printf和其他老式变量参数函数以及任何不安全的strcpy和类似函数李>
- 使用选中的STL版本
- 尝试未优化的发布版本
- 尝试优化调试版本
- 了解编译器的调试版本和发布版本之间的差异
- 从零开始重写有问题的代码。也许它不会有虫子
- 在数据崩溃时检查数据
- 检查错误/异常处理,看看是否忽略了某些潜在问题
- 测试程序在内存不足、磁盘空间不足、引发异常时的行为
- 确保调试器在每个抛出的异常被处理或未被处理时停止
- 确保您的程序在编译和运行时没有警告,或者您理解这些警告,并且确保这些警告无关紧要
- 当数据崩溃时,检查数据是否正常
- 您可以保留内存以减少碎片和重新分配。如果程序运行数小时,可能是内存碎片太多,系统找不到足够大的块
- 由于您的程序是多线程的,请确保您的运行时也与此兼容
- 确保您不跨线程共享数据,或确保数据得到充分保护
- 使用单个线程运行生产版本更长的时间(比如一周或两周),看看它是否崩溃
- 考虑到可能存在内存碎片,请确保不消耗所有可用RAM
- 确保程序运行的时间越长,不会出现内存泄漏或增加内存使用
- 通过添加额外的值来添加一些跟踪,将值设置为析构函数中已知的值(这样,如果执行双重删除,您可以识别该模式)
- 尝试在其他平台和编译器下运行该程序
- 您的编译器或库可能包含错误。请尝试另一个(较新的)版本
- 从原始版本中删除代码,直到不再崩溃。如果您能够始终使用某种程度上损坏内存的序列来获得崩溃,那么效果会更好
- 一旦发生崩溃,请使用完全相同的数据(针对每个线程)运行程序,并查看它是否总是在同一位置崩溃
- 重写或验证应用程序中的任何不安全代码。避免使用casting、printf和其他老式变量参数函数以及任何不安全的strcpy和类似函数李>
- 使用选中的STL版本
- 尝试未优化的发布版本
- 尝试优化调试版本
- 了解编译器的调试版本和发布版本之间的差异
- 从零开始重写有问题的代码。也许它不会有虫子
- 在数据崩溃时检查数据
- 检查错误/异常处理,看看是否忽略了某些潜在问题
- 测试程序在内存不足、磁盘空间不足、引发异常时的行为
- 确保调试器在每次抛出时停止,除了
frame #0: 0x00007fff769a72f6 libsystem_kernel.dylib`__pthread_kill + 10 frame #1: 0x00000001004aa80d libsystem_pthread.dylib`pthread_kill + 284 frame #2: 0x00007fff769116a6 libsystem_c.dylib`abort + 127 frame #3: 0x00007fff76a1f977 libsystem_malloc.dylib`malloc_vreport + 545 frame #4: 0x00007fff76a1f738 libsystem_malloc.dylib`malloc_report + 151 frame #5: 0x0000000100069448 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::__libcpp_deallocate(__ptr=<unavailable>) at new:236 [opt] frame #6: 0x0000000100069443 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::allocator<char>::deallocate(__p=<unavailable>) at memory:1796 [opt] frame #7: 0x0000000100069443 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::allocator_traits<std::__1::allocator<char> >::deallocate(__p=<unavailable>) at memory:1555 [opt] frame #8: 0x0000000100069443 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::~basic_string() at string:1941 [opt] frame #9: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::~basic_string() at string:1936 [opt] frame #10: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] CrashClass::~CrashClass(this=<unavailable>) at CrashClass.h:61 [opt] frame #11: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] CrashClass::~CrashClass(this=<unavailable>) at CrashClass.h:61 [opt] frame #12: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::allocator<CrashClass>::destroy(this=<unavailable>, __p=<unavailable>) at memory:1860 [opt] frame #13: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] void std::__1::allocator_traits<std::__1::allocator<CrashClass> >::__destroy<CrashClass>(__a=<unavailable>, __p=<unavailable>) at memory:1727 [opt] frame #14: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] void std::__1::allocator_traits<std::__1::allocator<CrashClass> >::destroy<CrashClass>(__a=<unavailable>, __p=<unavailable>) at memory:1595 [opt] frame #15: 0x0000000100069439 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::__vector_base<CrashClass, std::__1::allocator<CrashClass> >::__destruct_at_end(this=<unavailable>, __new_last=0x00000001011ad000) at vector:413 [opt] frame #16: 0x0000000100069429 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::__vector_base<CrashClass, std::__1::allocator<CrashClass> >::clear(this=<unavailable>) at vector:356 [opt] frame #17: 0x0000000100069422 BackTester`MyClass::DoStuff(int, int) [inlined] std::__1::vector<CrashClass, std::__1::allocator<CrashClass> >::clear(this=<unavailable>) at vector:749 [opt]