总结下,需要注意的是对于多个模块的开发,确保该模块的malloc自己free就OK了。

引用自

https://zhuanlan.zhihu.com/p/20628410?refer=jilinxiaohuo

https://www.zhihu.com/question/45753516

单例模式是一种很简单常用的设计模式,常见的做法可能是这样:

Renderer& getInstance()
{static Renderer renderer;return renderer;
}

当然,这个代码在不支持static local variable thread-safe init的编译器上,是没有办法保证线程安全的,c++11标准已经规定static local variable只会被初始化一次了,然而vs2013还没有实现,vs2015里才支持了这条标准.不过这条代码在我们的程序里只有一个线程访问,所以也就不存在线程安全的问题.

然后有一天,写了点代码,程序崩溃了:

Renderer::getInstance().xxxx();

分析发现是Renderer里的一个成员变量m_pMap == NULL了,而在项目的其他地方,也有用这个xxxx()函数的地方,竟然没有问题.

那就在xxxx()里下个断点,然后看下this指针的值吧,第一次命中时this的值是0x0456adc4, 第二次命中的时候是0x074324ad(这个地址是我随便写的,反正就是这两次命中断点this的值不一样).这是怎么回事呢?为什么出现了两个Renderer的实例呢?

细心的我发现这两个this指向的Renderer实例并没有在同一个模块内,一个在a.dll里,一个在main.exe里!造成这种现象的原因,是因为开篇的那个getInstance()方法所在工程是一个静态库,然后main.exe工程和a.dll工程均链接了这个静态库,导致main.exe里和a.dll里都存在一个renderer实例.而我们这个renderer实例在使用前,要这样:

Renderer::getInstance().Create();

然后才可以初始化m_pMap, 崩溃在main.exe里那行代码之前,并没有调用Create(),所以导致m_pMap == NULL崩溃了.

问题到这里已经水落石出了,想办法弄成在两个模块里共用一个实例就可以了!问题解决.

当然,如果文章就这样结束了,未免太没劲儿了吧,顺便说说另一个大家经常忽略的事情.

在开发dll的过程中,总会有意无意的写出一些跨模块分配释放内存的代码,比如在A模块malloc了一块内存,在B模块free,然后导致崩溃.然后将编译器选项由/MT改为/MD就可以解决问题.为什么会出现这种问题呢?我们来看看malloc的实现(vs2013 crt):

__forceinline void * __cdecl _heap_alloc (size_t size)
{if (_crtheap == 0) {
#if !defined (_CRT_APP) || defined (_DEBUG)_FF_MSGBANNER();    /* write run-time error banner */_NMSG_WRITE(_RT_CRT_NOTINIT);  /* write message */
#endif  /* !defined (_CRT_APP) || defined (_DEBUG) */__crtExitProcess(255);  /* normally _exit(255) */}return HeapAlloc(_crtheap, 0, size ? size : 1);
}

malloc最后会调用HeapAlloc来分配内存,msdn看看HeapAlloc的说明,

HeapAlloc Function

Allocates a block of memory from a heap. The allocated memory is not movable.

Syntax

LPVOID WINAPI HeapAlloc(__in  HANDLE hHeap,__in  DWORD dwFlags,__in  SIZE_T dwBytes
);

Parameters

hHeap A handle to the heap from which the memory will be allocated. This handle is returned by the HeapCreate or GetProcessHeap function.

,对着msdn的说明可以知道,malloc用的heap handle是 _crtheap,这个_crtheap是个全局变量,那我们看看是什么时候给_crtheap赋值的吧. 跟着红色箭头从上往下分析,首先在BaseThreadInitThunk上下个断点,然后F5执行,断点命中之后执行

x *!*_crtheap*

找到crtheap的地址,结果显示在0f7ec190这里,这里的值目前也是0x00000000,证明还没有被赋值,那就在这个地址上打个写断点,

ba w4 0f7ec190

F5执行,断点命中,输入kb显示调用栈,结果发现是在_heap_init里对_crtheap赋值的,看看heap_init的代码吧:

int __cdecl _heap_init (void)
{//  Initialize the "big-block" heap first.if ( (_crtheap = GetProcessHeap()) == NULL )return 0;return 1;
}

只是调用GetProcessHeap而已,那还得看看GetProcessHeap的实现:

KERNELBASE!GetProcessHeap:
75415620 64a118000000    mov     eax,dword ptr fs:[00000018h]
75415626 8b4030          mov     eax,dword ptr [eax+30h]
75415629 8b4018          mov     eax,dword ptr [eax+18h]
7541562c c3              ret

分析汇编可知,首先去TEB + 0x18里取值赋值给eax,根据上图可知,0x18为Self指针,其实就是TEB自己的地址,然后去TEB+0x30里取值赋值给eax,由图可知,0x30为ProcessEnvironmentBlock,即PEB, 然后去PEB+0x18里取值作为返回值,也就是ProcessHeap(见下图),那我们看看PEB+0x18是多少,
由图可知GetProcessHeap会返回005d0000,然后赋值给_crtheap.那和/MT /MD有什么关系呢?

/MT:

Causes your application to use the multithread, static version of the run-time library.

/MD:

Causes your application to use the multithread- and DLL-specific version of the run-time library.

如果使用/MT,会使用静态版本的运行时库,每个模块里都会有一个_crtheap全局变量,_heap_init就会在每个模块里都被调用一次,而使用/MD则使用动态库版本的运行时库,整个进程里只有运行时库里才有一份_crtheap全局变量,在crt的dll加载的时候调用一次_heap_init.但是分析_heap_init的实现可知,就算调用多次,返回的依然是PEB里的那个堆句柄啊,也不会导致不同模块里的_crtheap有不同的值,那HeapFree和HeapAlloc使用的handle就是同一个,也不应该引起崩溃啊.事实的确如此,我也是在今天写这篇专栏用windbg分析的时候才发现这个问题.是我之前记错了还是crt的实现有变化呢?于是我查看了下vs2003的crt的_heap_init的实现:

int __cdecl _heap_init (int mtflag)
{//  Initialize the "big-block" heap first.if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE,BYTES_PER_PAGE, 0 )) == NULL )return 0;#ifndef _WIN64// Pick a heap, any heap__active_heap = __heap_select();if ( __active_heap == __V6_HEAP ){//  Initialize the small-block heapif (__sbh_heap_init(MAX_ALLOC_DATA_SIZE) == 0){HeapDestroy(_crtheap);return 0;}}
#ifdef CRTDLLelse if ( __active_heap == __V5_HEAP ){if ( __old_sbh_new_region() == NULL ){HeapDestroy( _crtheap );return 0;}}
#endif  /* CRTDLL */
#endif  /* _WIN64 */return 1;
}

这就明了了,如果在vs2003里开发dll,然后编译dll的时候选择/MT,那这个dll内部就会有一份_crtheap,那在加载这个dll的时候,会调用一次_heap_init,然后调用HeapCreate来初始化_crtheap.这也就意味着其他模块使用/MT选项也会如此,导致每个模块里的_crtheap值是不同的.这样跨模块执行free,就会导致在handle A上分配的内存去handle B上释放导致了崩溃.如果所有模块均使用/MD选项,则只有crt的dll里有一份_crtheap,在crt的dll被加载的时候执行一次HeapCreate并赋值给_crtheap,然后HeapAlloc与 HeapFree使用的HANDLE都是同一个_crtheap,就不会崩溃了.

问题到这里就分析完毕了。

静态库那些事儿/MT /MD相关推荐

  1. 静态库、动态库、静态链接、动态链接、系统运行库混合、MD MT默认库冲突问题

    一.静态库项目 静态库lib:(注意和"静态运行库"区分)   就是.lib文件,一个.c或.cpp会编译成一个.obj,多个.obj可以组合成一个.lib库.lib=多个obj. ...

  2. vs编译c语言停止工作运行库mt,vc++编译时运行库选择(/MT/MTd/MD/MDd)

    vc++编译时运行库选择(/MT/MTd/MD/MDd) vc++编译时运行库选择(/MT./MTd./MD./MDd) 在vs中,项目属性 ->C/C++ ->代码生成 ->运行库 ...

  3. linux系统中 库分为静态库和,Linux系统中“动态库”和“静态库”那点事儿-【经典好文】...

    今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在这之前,我们需要了解一下源代码到可执行程序之间到底发生了什么神奇而美妙的事情. 在linux操作系统中,普遍使用E ...

  4. Linux系统中“动态库”和“静态库”那点事儿

    转自:https://blog.csdn.net/u010977122/article/details/52958330#commentBox 今天忙着编译库文件,中间遇到不少问题,看到这篇文章不错, ...

  5. Linux系统中“动态库”和“静态库”那点事儿【转】

    转自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻. ...

  6. windows动态库和静态库VS导入

    1. 静态库和动态库 1.1 静态库(.lib) 函数和数据被编译进一个二进制文件(通常扩展名为.LIB).在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程 ...

  7. C++ 中的动态库和静态库(Windows)

    库: 在C/C++中,使用库(Library)的技术,可以将编译好的符号提供给第三方使用. 库有两种: 1.动态库 Dynamic-Link Library (DLL)    (Linux下叫做 Sh ...

  8. 静态编译,动态编译,静态库,动态库的区别

    from :http://blog.csdn.net/ithzhang/article/details/20160009 周五晚,小雨,少见的未加班.无聊,遂准备写一篇博客,介绍一下C和C++运行库, ...

  9. VS2010 编译 SpiderMonkey 1.8.5 静态库版本

    大家好,前段时间看到 VC驿站 上面会员发布了一篇文章<Windows系统编译制作SpiderMonkey最新版mozjs-31.2.0版本>,地址为: http://www.cctry. ...

最新文章

  1. 数字证书管理工具keytool常用命令介绍
  2. 原来带有python又装了一个anaconda有影响吗_anaconda python环境与原有python环境的坑...
  3. 光纤布拉格光栅matlab,matlab对各种光纤光栅的仿真
  4. 无法通过sak判断卡片类型_不同类型人脸识别闸机展示
  5. vue-cli脚手架的.babelrc文件
  6. 魔兽世界阿拉索人数最多服务器,魔兽世界8.3哪个区人多_wow8.3服务器人数统计介绍_3DM网游...
  7. python 小说 云_python小说网站
  8. 计算机图形学图形旋转_计算机图形学翻译
  9. vs2008保存超级慢
  10. Android 之 Window、WindowManager 与窗口管理
  11. 技术系统优化还可以这样做?
  12. Revealing图片展示效果(jQuery)
  13. MemReduct内存自动清理工具
  14. b站视频怎么引流?b站做什么视频有流量?b站流量引流技巧
  15. 二元二次方程例题_二元二次方程组解法例题
  16. windows11安装日语输入法(添加输入法)
  17. 右击文件转圈卡住、刷新、白屏、闪退、桌面崩溃的通用解决方法
  18. python里面pow是什么意思_pow在python中是什么意思
  19. TDengine:开源、高效的物联网大数据平台
  20. 写php什么梗,什么是php(php是什么梗)

热门文章

  1. 西北乱跑娃 --- python图像基本操作
  2. Unity 屏幕特效后期处理 OnRenderImage
  3. LM358与TL431验证
  4. 交换机为什么需要划分AP、AG和SW三个层?
  5. oracle bulk select,批量查询 Oracle的bulk collect用法
  6. 鲸探发布点评:8月4日发售宁乡北峰滩兽面纹大铙、陶球数字藏品
  7. 【CTF特训营】 Reverse篇 2.Reverse分析
  8. 学画画软件app推荐_超好用的学习绘画的APP
  9. 使用Linux系统搭建自己的个人网站,小白网站
  10. 动词常见三种变形方式总结