在Dll中申请了内存,现在想在Exe模块中释放这部分内存.但不成功.楼主ecore2()2001-01-12 09:49:00 在 VC/MFC / 基础类 提问
谢谢
问题点数:0、回复次数:17
Top
1 楼bugn(unknown& whoami)回复于 2001-01-12 10:17:00 得分 0 如果用了CRT的内存分配(new,delete,malloc,free,...),你的dll和exe是静态或动态连接CRT的时候必须一致。msvcrt.dll用了单独的一个heap的,跟你的静态连接crt时用的heap不相同。
project settings->c/c++->code generation->use runtime library->dll和exe选择要一致
Top
2 楼kinghan(懒散的牛)回复于 2001-01-12 10:19:00 得分 0 内存指针还有效吗?
Top
3 楼Smile_Tiger(笑面虎)回复于 2001-01-12 10:21:00 得分 0 如果在dll中是用GlobalAlloc()分配的内存,在exe中是可以釋放的
Top
4 楼ab(ab)回复于 2001-01-12 10:27:00 得分 0 用 new 和 delete 的内存不能跨模块。你可以:
从 DLL 导出一个释放内存的函数,在 EXE 里面凡是从 DLL 分配的内存都用它释放。
Top
5 楼bugn(unknown& whoami)回复于 2001-01-12 10:29:00 得分 0 有效的。crt是在heap中分配的,heap在同一个进程内是有效的。GlobalAlloc是在每个进程的默认heap种分配的,一个进程可以使用多个heap。
crt使用单独的heap,刚才我前面说的还有点问题,就是如果是静态连接CRT,那么CRT在每个模块都用一个单独的heap;如果动态连接CRT(msvcrt.dll),那么所有用msvcrt.dll的模块就共享一个heap。
Top
6 楼Robert2001(Robert2001)回复于 2001-01-12 11:25:00 得分 0 系统为每一个进程维护一个4G的虚拟内存,你这EXE里的指针所指向的内存根本就不是DLL中的!
最好在你的DLLMAIN 里去释放它。 如果想要实现数据共享的话 ,用内存CreatFileMapping
或者用 #pragma data_("Shared")
volatile You_value
#pragma data_seg()
#pragma comment (Linker,"/Sectioin:Shared,RWS")
Top
7 楼ciba(风子)回复于 2001-01-12 11:34:00 得分 0 正规的话应该在dll的dllMain函数里来处理,case DLL_PROCESS_ATTACH:
case DLL_PROCESS_DETACH:两种情况分别处理了dll创建和摧毁,可以在此时分配内存和释放内存。在Exe中释放dll的内存,如果该dll还有用,释放掉它的内存合理吗?
Top
8 楼kylewu(水皮)回复于 2001-01-12 12:09:00 得分 0 这个话题以前已经讨论过了,总之这种方法就是不规范,是造成非法操作的诱因之一,最好不要用。 :)
在dll中,new和delete的管理数据和exe是分开的,因此在dll中new的东西到了exe中,虽然指针不是非法,不过在delete的时候就会出错,如果在debug版会出assert fail,如果是release版,可能不会报错,不过很可能造成memory leak或者access violation。
Top
9 楼newx(Royale with Cheese)回复于 2001-01-12 13:02:00 得分 0 Robert2001:
你结论的后半句说得不对,对不同的进程而言内存指针不能互用,但EXE和加载的DLL同属于一个进程,是少数(唯一?)可以互用的例子。
ciba:“在Exe中释放dll的内存,如果该dll还有用,释放掉它的内存合理吗?”
当然不行!但这里问题不是这种情况。这里的问题是"如果该dll不再使用,释放掉它的内存合理,但为什么失败?"
kylewu:"总之这种方法就是不规范"
太武断了,这种机制没什么不规范的,但之所以出问题是实现不规范!
其他各位:你们的回答多多少少,局部或全部的,不能令人信服或由此产生怀疑。
可能是本人孤陋寡闻,请出示试验证据或来源出处(如bugn的多heap说),我们也好互相纠正,共同学习。
我同意bugn的 “如果用了CRT的内存分配(new,delete,malloc,free,...),你的dll和exe是静态或动态连接CRT的时候必须一致。”
失败可能因为,1)EXE和DLL中的new/delete在其中一处(EXE/DLL)被重载过,另一处(DLL/EXE)没有,2)或是因为bugn的多heap说(等待进一步证实),不管哪一种,下面的解法都一样。
即:如果你用了new/delete的话,请改成用::new/::delete(全局-未重载的-操作符)。
Top
10 楼bugn(unknown& whoami)回复于 2001-01-12 13:15:00 得分 0 多heap我原来写了个程序做heap walk测试过的,我也察看了CRT的代码(堆的初始化的启动代码中, 用了个静态变量保存heap handl供malloc使用),确实是这样的。
对于这种问题,我一般的解决办法是这样的:
dll输出类的new和delete运算符,这样无论你在那里调用都行(任意模块),都是在执行原来dll的malloc,确保用的是同样的heap。
示例:
#ifdef BUGN_EXPORT
#define BUGN_API __declspec(dllexport)
#else
#define BUGN_API __declspec(dllimport)
#endif
class CFoo{
public
BUGN_API void* operator new(size_t size) {return ::new char[size];}
BUGN_API void operator delete(void* p) {::delete p;}
}
Top
11 楼maze(迷)回复于 2001-01-12 15:10:00 得分 0 bugn(bugn)
你写的很有新意不错不错
我开始也是用一个输出函数来做的
不如你高
newx(生命在于不动)
说得不错值,非常同意
Top
12 楼casanova(旗舰)回复于 2001-01-12 18:23:00 得分 0 bugn说得不错。每个win32应用程序至少有一个操作系统提供的缺省堆。如果用到C
Runtime Library的程序(比如VC++编出来的程序),还有一个C运行库自己管理的堆。
此外,每个程序或DLL都可以自己创建私有堆。多处理器系统中,还可能有MP堆。
ecore2遇到的这种错误,一般说来都是因为分配和释放没有在一个堆上。(因为
一般没有人会犯把GlobalAlloc()分配出来的内存用delete释放一类的错误的吧)
所以我想这可能与你的DLL和EXE,在链接C运行库时的方式有关。比如DLL是静态链接
C运行时库,而EXE是动态链接,那么在运行时,DLL内分配的内存一定是分在了C运行
时堆;而EXE在释放时则要进行一番查找来决定用哪个函数。vc++ v6.0运行库既可以
使用其自己内部的堆管理函数,也可以直接调用操作系统的堆管理函数(heapalloc()
系列的函数)。如果是运行于nt或2000,则用heapalloc();否则检查环境变量
__global_heap_selected,以决定是用heapalloc,vc6堆函数还是vc5堆函数;还决定
不了的话,会检查文件连接标志,如果是由vc++ v6或更高的版本创建的,就使用版本6
的堆函数,否则使用版本5的堆函数。
Top
13 楼newx(Royale with Cheese)回复于 2001-01-12 23:55:00 得分 0 用类似COM的Reference count最好!它是解决由模块申请和释放其它模块的内存的法宝!
Top
14 楼kylewu(水皮)回复于 2001-01-13 17:03:00 得分 0 以前说这个问题,最后得出的结论也是用COM的IMalloc,最安全,最通用,不过效率最低。 :(
很多时候为了效率,还是需要用一些不太规范的方法,冒一些险。
至于多heap的问题,大概就是bugn说的那样了,不过我补充一下,就是如果dll用静态连接msvcrt.dll,那么dll的初始化代码会另外分配一个crt heap,如果用动态连接,就会用process原来的crt heap,exe的情况也一样。如果dll静态连接msvcrt.dll,就会有两个crt heap,那么dll中malloc的内存块对于exe的heap来说是非法的。这可以在CRTDLL.C和DLLCRT0.C里面看到其中的差别。因此并不能保证在dll中分配的内存在exe中能正确的释放。
我们写dll的目的之一就是代码重用,这种方法不能保证别的exe能不能正确的使用dll的资源,所以我认为这种方法不规范,如果是作为大项目的开发,这种方式不可取。因为其他程序员可能不清楚其中潜在的问题,很容易造成程序的不稳定。
我还是比较偏向于使用IMalloc的,可能写COM比较多的关系吧。如果不是特别频繁的分配/释放,IMalloc基本上不会造成明显的效率下降。至于频繁的分配/释放,还不如一开始开个缓冲区,减少分配/释放?
Top
15 楼bugn(unknown& whoami)回复于 2001-01-13 17:58:00 得分 0 to kylewu:
关于IMalloc确实是好办法,跟前面建议的GlobalAlloc(LocalAlloc)类似,不会出因为在不同的堆做分配和释放操作而出问题。
另外到提醒我的是你还不如建议他最好把程序结构改一下,把Dll实现的功能完全做成COM组件。关于COM是否会降低效率的问题,在我的项目里我曾仔细考虑过:如果精心的设计一下,减少借口的频繁调用(把功能重新划分),绝对不会有什么问题的。即使大量使用COM组件接口调用,那么开销会增大多少?如果仔细看一下COM的接口定义方式,会发现某种方式下跟普通的输出函数调用没有区别的。
Top
16 楼blackhorse18(黑马)回复于 2001-01-13 18:52:00 得分 0 俗话说:"解铃还需系铃人",从程序运行的可靠性来看,最好还是在DLL中增加一个相应的内存释放函数,在EXE中调用,这是比较稳妥的。
====
我试了一下。从动态库导出函数 fa(char**p){ *p=new char[123] };
在exe文件中有如下代码:
char* x=NULL;
fa(&x);
delete[] x;
发现不同的编译环境下运行结果不一样。
1. dll和exe 都用MFC Appwizard方式生成,那么运行时没有任何问题。如果把delete语句去掉,则VC报告内存泄漏 123 Bytes。说明一切正常。
2. 如果exe用 MFC Appwizard方式生成, dll用win32方式生成,则运行时会出现象你所说的错误。进一步用单步跟踪,发现mfc方式和win32方式下的new操作符是用不同方式实现的,源程序分别在VC目录的文件 Afxmem.cpp和new.cpp中。有兴趣的话可以自已跟踪一下。
因为dll输出函数后,并不知道是哪一个
模拟
调用它,因此new和delete配对时最好在一个文件中,这样可以保证一致性。就如你所说从dll中导出一对函数。
另一个
解决
方法是把dll和exe都用同一种方法编译(比如都用MFC方式)。我试了一下,把win32程序改为mfc程序并不困难。
希望对你有所帮助。
===