dll,内存管理

本文详细探讨了在Windows环境下,CRT堆的概念及其管理方式。解释了主程序与DLL间内存分配与释放的区别,以及不同模块间内存管理的重要性。强调了在多模块程序中统一CRT版本的必要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主程序和dll中的内存的分配和释放不是由相同的堆管理程序完成的,例如动态链接库中的堆在默认情况下是由msvcrt.dll中的堆管理程序管理的 (以动态链接的方式),而exe程序的堆在默认情况下是由程序自己的代码管理(以静态链接的方式)。

如果程序采用静态链接的方式,每一个可执行模块都会创建自己的CRT堆,在模块内部进行new和delete操作的时候,是在CRT堆上进行的,这样不会出错。但是如果在dll模块中申请存储,但是在主程序模块释放存储,就会出现不知道如何正确释放dll中分配的内存或者释放存储的行为是错误的。

首先CRT就是C RunTime的缩写,意思是C运行库。CRT可以理解为windows操作系统对C语言提供的一套支撑库,使得C程序通过C标准库函数就能与操作系统交互,而不需要调用windows API。普通CRT进程退出时,会首先以LIFO调用atexit注册的例程,执行完这些例程后,跟着清理CRT堆空间(而不是清理进程堆空间),接着调用ExitProcess退出。注意了,不要以为进入ExitProcess后,进程就退掉,其实在进入ExitProcess后,将调用已加载的Dll的DllMainCRTStartup,这个函数以Process_Detach调用加载到进程的Dll的DllMain函数,之后,还会调用Dll的atexit注册例程,之后又是清理Dll自身的CRT堆空间。CRT堆就供new和malloc分配内存的进程堆空间。一个重要的概念,MainCRTStartup(DllMainCRTStartup)会调用main(DllMain)、atexit序列及清理CRT堆空间(静态连接为前提)。

其实,每个Dll模块都有自己的atexit stack,这个stack只有在Process_Detach时才会调用,每个Dll的atexit stack单独维护,并且与进程的atexit stack毫无关系。所以,请注意,凡是Dll内注册的atexit,不要引用非自身模块的变量或者非自身模块的CRT堆空间(静态连接为前提),因为,Dll的atexit例程调用序列只按照自身Dll注册的序列调用,与外部的atexit序列不会产生任何关系。而引用的外部变量,很可能在Dll调用atexit注册的例程时已无效,特别是不要引用非自身CRT堆空间(静态连接为前提)。

 

CRT堆是我们使用new、malloc的基础,CRT堆在可执行模块的入口点(MainCRTStartup、DllMainCRTStartup)使用HeapCreate创建。基于win32虚地址机制,CRT堆对象的地址与进程默认堆是在同一个线性地址空间内,使得进程可以通过加减内存地址轻松访问。

我们现在知道CRT堆的由来,那么问题来了,当可执行模块是静态连接(或连接到不同版本的CRT运行库)的时候,这份创建CRT堆的代码会存在于各个的可执行模块内部,于是,每个可执行模块在加载时都会创建自己的堆,我把这种堆称为自身CRT堆。

当这些拥有自身CRT堆的可执行模块加载到同一个进程空间内的时候,它们的堆空间虽然能交叉访问,但却不能交叉释放(A模块分配的内存不能在B模块内释放),因为这些内存不是属于同一个堆的。同样,A模块分配的内存在A模块卸载后就变得无效了,因为A模块卸载会释放A模块自身CRT堆。当使用STL容器作模块间传递时,这些问题特别容易显现。想象一下容器内的元素来自不同的CRT堆,当容器析构时是怎么样的情况。

所以,当应用程序以可执行模块划分的时候,应该统一使用动态连接CRT运行库,并且,连接同一个版本的运行库。那么,什么是同一个版本的运行库呢?下一节展开分析。

话说回来,如果所有堆操作都使用进程默认堆,就不会出现问题了。不过,使用默认堆空间效率会比CRT堆低,因为CRT堆会预先保留提交内存以备使用,另外,使用进程默认堆,就不能使用new、delete创建销毁对象了。离开new,如何创建C++对象?^_^,使用placement new嘛。如何delete对象?先显式析构,再HeapFree吧。

http://www.cnblogs.com/rickerliang/archive/2011/09/14/2176656.html

内存加载动态库 MemoryLoadLibrary 有例子。 /* * Memory DLL loading code * Version 0.0.3 * * Copyright (c) 2004-2013 by Joachim Bauch / mail@joachim-bauch.de * http://www.joachim-bauch.de * * The contents of this file are subject to the Mozilla Public License Version * 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is MemoryModule.h * * The Initial Developer of the Original Code is Joachim Bauch. * * Portions created by Joachim Bauch are Copyright (C) 2004-2013 * Joachim Bauch. All Rights Reserved. * */ #ifndef __MEMORY_MODULE_HEADER #define __MEMORY_MODULE_HEADER #include typedef void *HMEMORYMODULE; typedef void *HMEMORYRSRC; typedef void *HCUSTOMMODULE; #ifdef __cplusplus extern "C" { #endif typedef HCUSTOMMODULE (*CustomLoadLibraryFunc)(LPCSTR, void *); typedef FARPROC (*CustomGetProcAddressFunc)(HCUSTOMMODULE, LPCSTR, void *); typedef void (*CustomFreeLibraryFunc)(HCUSTOMMODULE, void *); /** * Load DLL from memory location. * * All dependencies are resolved using default LoadLibrary/GetProcAddress * calls through the Windows API. */ HMEMORYMODULE MemoryLoadLibrary(const void *); /** * Load DLL from memory location using custom dependency resolvers. * * Dependencies will be resolved using passed callback methods. */ HMEMORYMODULE MemoryLoadLibraryEx(const void *, CustomLoadLibraryFunc, CustomGetProcAddressFunc, CustomFreeLibraryFunc, void *); /** * Get address of exported method. */ FARPROC MemoryGetProcAddress(HMEMORYMODULE, LPCSTR); /** * Free previously loaded DLL. */ void MemoryFreeLibrary(HMEMORYMODULE); /** * Find the location of
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值