精华内容
下载资源
问答
  • 共享内存DLL

    2012-09-28 17:19:18
    使用动态库实现内存共享的例子,动态库的大小和共享内存空间大小有关,使用起来不是太方便。
  • 通过图文描述介绍MFC与dll之间通过共享内存建立通信,文件内附有可直接复制的代码,文档和文档里的代码是基于VS2017编写的。前两天通过VS2019实现,文件里的代码在通过VS2019仍然可以实现。初学MFC与dll之间通信的...
  • [转] Delphi7 内存管理及 FastMM 研究 (对于EXE和DLL内存共享很有用) 故国之晚秋 发表于2010-12-06 19:34 浏览(32) 评论(0) 分类:我的日记  举报 引自:...

    [转] Delphi7 内存管理及 FastMM 研究 (对于EXE和DLL内存共享很有用)

    故国之晚秋 发表于2010-12-06 19:34 浏览(32) 评论(0) 分类: 我的日记      举报

    引自:http://hi.baidu.com/yehe2316/blog/item/d66d3b03b4440a064afb51f0.html

    Delphi7 内存管理及 FastMM 研究[转]
    作者:刘国辉
    一、引言
          FastMM 是适用于delphi的第三方内存管理器,在国外已经是大名鼎鼎,在国内也有许多人在使用或者希望使用,就连 Borland 也在delphi2007抛弃了自己原有的饱受指责的内存管理器,改用FastMM.
          但是,内存管理的复杂性以及缺乏 FastMM 中文文档导致国内许多人在使用时遇到了许多问题,一些人因此而放弃了使用,我在最近的一个项目中使用了FastMM,也因此遇到了许多问题,经过摸索和研究,终于解决了这些问题。

    二、为什么要用FastMM
    第一个原因是FastMM的性能接近与delphi缺省内存管理器的两倍,可以做一个简单的测试,运行下面的代码:
    var
    I: Integer;
    Tic: Cardinal;
    S: string;
    begin
    tic := GetTickCount;
    try
        for I := 0 to 100000 do
        begin
          SetLength(S, I + 100);
          edt1.Text := S;
        end;
    finally
        SetLength(S, 0);
        tic := GetTickCount - Tic;
        MessageDlg('Tic = ' + IntToStr(Tic), mtInformation, [mbOK], 0);
    end;
    end;
    在我的IBM T23笔记本上,使用FastMM4(FastMM的最新版本)用时约为3300ms,而使用缺省的内存管理器,用时约为6200ms,FastMM4的性能提高达88%.
    第二个原因FastMM的共享内存管理器功能使用简单可靠。当一个应用程序有多个模块(exe和dll)组成时,模块之间的动态内存变量如string的传递就是一个很大的问题,缺省情况下,各个模块都由自己的内存管理器,由一个内存管理器分配的内存也必须在这个内存管理器才能安全释放,否则就会出现内存错误,这样如果在一个模块分配的内存在另外一个模块释放就会出现内存错误。解决这个问题就需要使用到共享内存管理器,让各个模块都使用同一个内存管理器。Delphi缺省的共享内存管理器是BORLNDMM.DLL,这个内存管理器并不可靠,也常常出现问题,并且,在程序发布的时候必须连同这个DLL一起发布。而FastMM的共享内存管理器功能不需要DLL支持,并且更加可靠。
    第三个原因是FastMM还拥有一些帮助程序开发的辅助功能,如内存泄漏检测功能,可以检测程序是否存在未正确释放的内存等。

    三、出现什么问题
    如果我们开发的应用程序,只有一个exe模块,那么,使用FastMM是一件非常简单的事情,只需要把FastMM.pas(最新版是FastMM4.pas)作为工程文件的第一个uses单元即可,如:

    program Test;
    uses
        FastMM4,
        …
    但是,通常情况下,我们的应用程序都是由一个exe模块加上多个dll组成的,这样,当我们跨模块传递动态内存变量如string变量时,就会出问题,比如,下面的测试程序由一个exe和一个dll组成:

    library test;   // test.dll
    uses
        FastMM4, …;
    procedure GetStr(var S: string; const Len: Integer); stdcall;
    begin
        SetLength(S, Len); // 分配内存
        FillChar(S[1], Len, ‘A’);  
    end;
    exports
        GetStr;
    ————————————————————————————————————-
    program TestPrj;
    uses
        FastMM4, …;
    //——————————————————
    unit mMain; // 测试界面

    procedure TForm1.btnDoClick(Sender: TObject);
    var
    I: Integer;
    S: string;
    Begin
    try
    for I := 1 to 10000 do
    begin
        GetStr(S, I + 1);
        edt1.Text := S;
        Application.ProcessMessages;
    end;
    finally
        SetLength(S, 0);
    end;
    end;

    当第二次执行btnDoClick过程时,就会出现内存错误,为什么这样?delphi的字符串是带引用计数的,跟接口变量一样,一旦这个引用计数为0,则会自动释放内存。在btnDoClick过程中,调用GetStr过程,用SetLength给S分配了一段内存,此时这个字符串的引用计数为1,然后执行edt1.Text := S语句,字符串的引用计数为2,循环再调用GetStr给S重新分配内存,这样原来的字符串的引用计数减1,再执行edt1.Text := S,原来的字符串引用计数为0,这时,就会被释放(注意,是在TestPrj.exe释放,而不是在Test.dll释放),但这时没有出错,当循环执行完毕之后,还有一个字符串的引用计数为2,但是执行SetLength(S, 0)之后,该字符串被edt1.Text引用,的引用计数为1,第二次执行btnDoClick时,执行edt1.Text := S时,上次的引用计数为1的字符串引用计数减一变为0,就会被释放,此时,会出现内存错误。
    由此,可以看到,在另一个模块释放别的模块分配的内存,并不一定马上出现内存错误,但是,如果频繁执行,则会出现内存错误,这种不确定的错误带有很大的隐蔽性,常常在调试时不出现,但实际应用时出现,不仔细分析很难找到原因。
    要解决这个问题,就要从根源找起,这个根源就是内存管理。
    一、Delphi的内存管理
    Delphi应用程序可以使用的有三种内存区:全局内存区、堆、栈,全局内存区存储全局变量、栈用来传递参数以及返回值,以及函数内的临时变量,这两种都是由编译器自动管理,而如字符串、对象、动态数组等都是从堆中分配的,内存管理就是指对堆内存管理,即从堆中分配内存和释放从堆中分配的内存(以下称内存的分配和释放)。
    我们知道,一个进程只有一个栈,因此,也很容易误以为一个进程也只有一个堆,但实际上,一个进程除了拥有一个系统分配的默认堆(默认大小1MB),还可以创建多个用户堆,每个堆都有自己的句柄,delphi的内存管理所管理的正是自行创建的堆,delphi还把一个堆以链表的形式分成多个大小不等的块,实际的内存操作都是在这些块上。
    delphi把内存管理定义为内存的分配(Get)、释放(Free)和重新分配(Realloc)。内存管理器也就是这三种实现的一个组合,delphi在system单元中定义了这个内存管理器TMemoryManager:

    PMemoryManager = ^TMemoryManager;
    TMemoryManager = record
        GetMem: function (Size: Integer): Pointer;
        FreeMem: function (P: Pointer): Integer;
        ReallocMem: function (P: Pointer; Size: Integer): Pointer;
    end;
    由此知道,delphi的内存管理器就是一个 TMemoryManager 记录对象,该记录有三个域,分别指向内存的分配、释放和重新分配例程。
    System单元还定义了一个变量 MemoryManager :
    MemoryManager: TMemoryManager = (
        GetMem: SysGetMem;
        FreeMem: SysFreeMem;
        ReallocMem: SysReallocMem);
    该变量是delphi程序的内存管理器,缺省情况下,这个内存管理器的三个域分别指向GETMEM.INC中实现的SysGetMem、SysFreeMem、SysReallocMem。这个内存管理器变量只在system.pas中可见,但是system单元提供了三个可以访问该变量的例程:

    // 读取内存管理器,也即读取MemoryManager
    procedure GetMemoryManager (var MemMgr: TMemoryManager);
    // 安装内存管理器(即用新的内存管理器替换缺省的内存管理器)
    procedure SetMemoryManager (const MemMgr: TMemoryManager);
    // 是否已经安装了内存管理器(即缺省的内存管理器是否已经被替换)
    function IsMemoryManagerSet: Boolean;

    四、共享内存管理器
    什么是共享内存管理器?
    所谓共享内存管理器,就是一个应用程序的所有的模块,不管是exe还是dll,都使用同一个内存管理器来管理内存,这样,内存的分配和释放都是同一个内存管理器完成的,就不会出现内存错误的问题。
    那么如何共享内存管理器呢?
    由上分析,我们可以知道,既然要使用同一个内存管理器,那么干脆就创建一个独立的内存管理器模块(dll),其他的所有模块都使用这个模块的内存管理器来分配和释放内存。Delphi7默认就是采取这种方法,当我们使用向导创建一个dll工程时,工程文件会有这样一段话:
    {Important note about DLL memory management: ShareMem must be the
    first unit in your library's USES clause AND your project's (select
    Project-View Source) USES clause if your DLL exports any procedures or
    functions that pass strings as parameters or function results. This
    applies to all strings passed to and from your DLL——even those that
    are nested in records and classes. ShareMem is the interface unit to
    the BORLNDMM.DLL shared memory manager, which must be deployed along
    with your DLL. To avoid using BORLNDMM.DLL, pass string information
    using PChar or ShortString parameters. }
    这段话提示我们,ShareMem 是 BORLNDMM.DLL 共享内存管理器的接口单元,我们来看看这个ShareMem,这个单元文件很简短,其中有这样的声明:

    const
    DelphiMM = 'borlndmm.dll';
    function SysGetMem (Size: Integer): Pointer;
    external DelphiMM name '@Borlndmm@SysGetMem$qqri';
    function SysFreeMem(P: Pointer): Integer;
    external DelphiMM name '@Borlndmm@SysFreeMem$qqrpv';
    function SysReallocMem(P: Pointer; Size: Integer): Pointer;
    external DelphiMM name '@Borlndmm@SysReallocMem$qqrpvi';
    这些声明保证了BORLNDMM.DLL将被静态加载。
    在ShareMem的Initialization是这样的代码:
    if not IsMemoryManagerSet then
        InitMemoryManager;
    首先判断内存管理器是否已经被安装(也即是否默认的内存管理器被替换掉),如果没有,则初始化内存管理器,InitMemoryManager也非常简单(把无用的代码去掉了):

    procedure InitMemoryManager;
    var
    SharedMemoryManager: TMemoryManager;
    MM: Integer;
    begin
    // force a static reference to borlndmm.dll, so we don't have to LoadLibrary
    SharedMemoryManager.GetMem:= SysGetMem;
    MM: = GetModuleHandle (DelphiMM);
    SharedMemoryManager.GetMem:= GetProcAddress (MM,'@Borlndmm@SysGetMem$qqri');
    SharedMemoryManager.FreeMem:= GetProcAddress (MM,'@Borlndmm@SysFreeMem$qqrpv');
    SharedMemoryManager.ReallocMem:= GetProcAddress (MM, '@Borlndmm@SysReallocMem$qqrpvi');
    SetMemoryManager (SharedMemoryManager);
    end;
    这个函数定义了一个内存管理器对象,并设置域指向Borlndmm.dll的三个函数实现,然后调用SetMemoryManager来替换默认的内存管理器。
    这样,不管那个模块,因为都要将ShareMem作为工程的第一个uses单元,因此,每个模块的ShareMem的Initialization都是最先被执行的,也就是说,每个模块的内存管理器对象虽然不相同,但是,内存管理器的三个函数指针都是指向Borlndmm.dll的函数实现,因此,所有模块的内存分配和释放都是在Borlndmm.dll内部完成的,这样就不会出现跨模块释放内存导致错误的问题。
    那么,FastMM又是如何实现共享内存管理器呢?
    FastMM采取了一个原理上很简单的办法,就是创建一个内存管理器,然后将这个内存管理器的地址放到一个进程内所有模块都能读取到的位置,这样,其他模块在创建内存管理器之前,先查查是否有别的模块已经把内存管理器放到这个位置,如果是则使用这个内存管理器,否则才创建一个新的内存管理器,并将地址放到这个位置,这样,这个进程的所有模块都使用一个内存管理器,实现了内存管理器的共享。
    而且,这个内存管理器并不确定是哪个模块创建的,所有的模块,只要将FastMM作为其工程文件的第一个uses单元,就有可能是这个内存管理器的创建者,关键是看其在应用程序的加载顺序,第一个被加载的模块将成为内存管理器的创建者。
    那么,FastMM具体是如何实现的呢?
    打开 FastMM4.pas(FastMM的最新版本),还是看看其Initialization部分:

    {Initialize all the lookup tables, etc. for the memory manager}
    InitializeMemoryManager;
    {Has another MM been set, or has the Borland MM been used? If so, this file
       is not the first unit in the uses clause of the project's .dpr file.}
    if CheckCanInstallMemoryManager then
    begin
        InstallMemoryManager;
    end;
    InitializeMemoryManager 是初始化一些变量,完成之后就调用CheckCanInstallMemoryManager检测FastMM是否是作为工程的第一个uses单元,如果返回True,则调用InstallMemoryManager安装FastMM自己的内存管理器,我们按顺序摘取该函数的关键性代码进行分析:
    {Build a string identifying the current process}
    LCurrentProcessID: = GetCurrentProcessId;
    for i := 0 to 7 do
    UniqueProcessIDString [8 - i]:= HexTable [((LCurrentProcessID shr (i * 4)) and $F)];
    MMWindow: = FindWindow ('STATIC', PChar (@UniqueProcessIDString [1]));
    首先,获取该进程的ID,并转换为十六进制字符串,然后查找以该字符串为窗口名称的窗口。
    如果进程中还没有该窗口,则MMWindow 将返回0,那就,就创建该窗口:
    MMWindow: = CreateWindow ('STATIC', PChar (@UniqueProcessIDString [1]),
              WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
    创建这个窗口有什么用呢,继续看下面的代码:

    if MMWindow <> 0 then
    SetWindowLong (MMWindow, GWL_USERDATA, Integer (@NewMemoryManager));
    NewMemoryManager.Getmem: = FastGetMem;
    NewMemoryManager.FreeMem: = FastFreeMem;
    NewMemoryManager.ReallocMem: = FastReallocMem;
    查阅MSDN可以知道,每个窗口都有一个可供创建它的应用程序使用的32位的值,该值可以通过以GWL_USERDATA为参数调用SetWindowLong来进行设置,也可以通过以GWL_USERDATA为参数调用GetWindowLong来读取。由此,我们就很清楚地知道,原来FastMM把要共享的内存管理器的地址保存到这个值上,这样其他模块就可以通过GetWindowLong来获读取到这个值,从而得到共享的内存管理器:

    NewMemoryManager: = PMemoryManager (GetWindowLong (MMWindow, GWL_USERDATA)) ^;
    通过上面的分析,可以看出,FastMM 在实现共享内存管理器上要比borland巧妙得多,borland的实现方法使得应用程序必须将BORLNDMM.DLL一起发布,而FastMM的实现方法不需要任何dll的支持。
    但是,上面的摘录代码把一些编译选项判断忽略掉了,实际上,要使用FastMM的共享内存管理器功能,需要在各个模块编译的时候在FastMM4.pas单元上打开一些编译选项:
    {$define ShareMM} //是打开共享内存管理器功能,是其他两个编译选项生效的前提
    {$define ShareMMIfLibrary} //是允许一个dll共享其内存管理器,如果没有定义这个选项,则一个应用程序中,只有exe模块才能够创建和共享其内存管理器,由于静态加载的dll总是比exe更早被加载,因此,如果一个dll会被静态加载,则必须打开该选项,否则可能会出错
    {$define AttemptToUseSharedMM} //是允许一个模块使用别的模块共享的内存管理器
    这些编译选项在FastMM4.pas所在目录的FastMM4Options.inc文件中都有定义和说明,只不过这些定义都被注释掉了,因此,可以取消注释来打开这些编译选项,或者可以在你的工程目录下创建一个.inc文件(如FastShareMM.inc),把这些编译选项写入这个文件中,然后,在FastMM4.pas开头的“{$Include FastMM4Options.inc}”之前加入“{$include FastShareMM.inc}”即可,这样,不同的工程可以使用不同的FastShareMM.inc文件。

    五、多线程下的内存管理
    多线程环境下,内存管理是安全的吗?显然,如果不采取一定的措施,那么肯定是不安全的,borland已经考虑到这种情况,因此,在delphi的system.pas中定义了一个系统变量IsMultiThread,这个系统变量指示当前是否为多线程环境,那么,它是如何工作的?打开TThread.Create函数的代码可以看到它调用了BeginThread来创建一个线程,而BeginThread把IsMultiThread设置为了True.
    再来看看GETMEM.INC的SysGetMem、SysFreeMem、SysReallocMem的实现,可以看到,在开始都由这样的语句:
    if IsMultiThread then EnterCriticalSection(heapLock);
    也就是说,在多线程环境下,内存的分配和释放都要用临界区进行同步以保证安全。
    而FastMM则使用了一条CUP指令lock来实现同步,该指令作为其他指令的前缀,可以在在一条指令的执行过程中将总线锁住,当然,也是在IsMultiThread为True的情况下才会进行同步。
    而IsMultiThread是定义在system.pas的系统变量,每个模块(exe或者dll)都有自己的IsMultiThread变量,并且,默认为Fasle,只有该模块中创建了用户线程,才会把这个变量设置为True,因此,我们在exe中创建线程,只会把exe中的IsMultiThread设置为True,并不会把其他的dll模块中的IsMultiThread设置为True,但是,前面已经说过,如果我们使用了静态加载的dll,这些dll将会比exe更早被系统加载,这时,第一个被加载的dll就会创建一个内存管理器并共享出来,其他模块都会使用这个内存管理器,也就是说,exe的IsMultiThread变量没有影响到应用程序的内存管理器,内存管理器还是认为当前不是多线程环境,因此,没有进行同步,这样就会出现内存错误的情况。
    解决这个问题就是要想办法当处于多线程环境时,让所有的模块的IsMultiThread都设置为True,以保证不管哪个模块实际创建了内存管理器,该内存管理器都知道当前是多线程环境,需要进行同步处理。
    还好,windows提供了一个机制,可以让我们的dll知道应用程序创建了线程。DllMain函数是dll动态链接库的入口函数,delphi把这个入口函数封装起来,提供了一个TDllProc的函数类型和一个该类型的变量DllProc:

    TDLLProc = procedure (Reason: Integer); // 定义在system.pas
    // 定义在sysinit.pas:
    var
        DllProc: TDllProc;

    当系统调用dll的DllMain时,delphi最后会调用DllProc进行处理,DllProc可以被指向我们自己的TDllProc实现。而当进程创建了一个新线程时,操作系统会以Reason=DLL_THREAD_ATTACH为参数调用DllMain,那么delphi最后会以该参数调用DllProc,因此我们只要实现一个新的TDllProc实现ThisDllProc,并将DllProc指向ThisDllProc,而在ThisDllProc中,当收到DLL_THREAD_ATTACH时把IsMultiThread设置为True即可。实现代码如下:

    library xxx;
    var
    OldDllProc: TDLLProc;
    procedure ThisDllProc(Reason: Integer);
    begin
    if Reason = DLL_THREAD_ATTACH then
        IsMultiThread := True;
    if Assigned(OldDllProc) then
        OldDllProc(Reason);
    end;
    begin
    OldDllProc := DllProc;
    DllProc := ThisDllProc;
    ThisDllProc(DLL_PROCESS_ATTACH);


    六、总结
    本文主要讨论了下面几个问题:
        1、为什么要使用FastMM
        2、跨模块传递动态内存变量会出现什么问题,原因是什么
        3、delphi的内存管理和内存管理器是怎么回事
         4、为什么要共享内存管理器,delphi和FastMM分别是如何实现内存管理器共享的
         5、多线程环境下,内存管理器如何实现同步
         6、多线程环境下,如何跨模块设置IsMultiThread变量以保证内存管理器会进行同步

    要正确使用FastMM,在模块开发的时候需要完成以下工作:
        1、打开编译选项{$define ShareMM}、{$define ShareMMIfLibrary}、{$define AttemptToUseSharedMM}
        2、将FastMM(4).pas作为每个工程文件的第一个uses单元
        3、如果是dll,需要处理以DLL_THREAD_ATTACH为参数的DllMain调用,设置IsMultiThread为True

    七、参考文献
    《Windows 程序设计第五版》[美]Charles Petzold著,北京大学出版社
    《Delphi源代码分析》 周爱民 著,电子工业出版社

    展开全文
  • c# 用共享内存实现进程通信

    热门讨论 2012-09-06 16:34:04
    c# 用共享内存实现进程通信 开几个程序都可以访问同样的内存数据 [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint ...
  • [转] Delphi7 内存管理及 FastMM 研究 (对于EXE和DLL内存共享很有用) 故国之晚秋 发表于2010-12-06 19:34 浏览(32) 评论(0) 分类:我的日记  举报 引自:...

    [转] Delphi7 内存管理及 FastMM 研究 (对于EXE和DLL内存共享很有用)

    故国之晚秋 发表于2010-12-06 19:34 浏览(32) 评论(0) 分类: 我的日记      举报

    引自:http://hi.baidu.com/yehe2316/blog/item/d66d3b03b4440a064afb51f0.html

    Delphi7 内存管理及 FastMM 研究[转]
    作者:刘国辉
    一、引言
          FastMM 是适用于delphi的第三方内存管理器,在国外已经是大名鼎鼎,在国内也有许多人在使用或者希望使用,就连 Borland 也在delphi2007抛弃了自己原有的饱受指责的内存管理器,改用FastMM.
          但是,内存管理的复杂性以及缺乏 FastMM 中文文档导致国内许多人在使用时遇到了许多问题,一些人因此而放弃了使用,我在最近的一个项目中使用了FastMM,也因此遇到了许多问题,经过摸索和研究,终于解决了这些问题。

    二、为什么要用FastMM
    第一个原因是FastMM的性能接近与delphi缺省内存管理器的两倍,可以做一个简单的测试,运行下面的代码:
    var
    I: Integer;
    Tic: Cardinal;
    S: string;
    begin
    tic := GetTickCount;
    try
        for I := 0 to 100000 do
        begin
          SetLength(S, I + 100);
          edt1.Text := S;
        end;
    finally
        SetLength(S, 0);
        tic := GetTickCount - Tic;
        MessageDlg('Tic = ' + IntToStr(Tic), mtInformation, [mbOK], 0);
    end;
    end;
    在我的IBM T23笔记本上,使用FastMM4(FastMM的最新版本)用时约为3300ms,而使用缺省的内存管理器,用时约为6200ms,FastMM4的性能提高达88%.
    第二个原因FastMM的共享内存管理器功能使用简单可靠。当一个应用程序有多个模块(exe和dll)组成时,模块之间的动态内存变量如string的传递就是一个很大的问题,缺省情况下,各个模块都由自己的内存管理器,由一个内存管理器分配的内存也必须在这个内存管理器才能安全释放,否则就会出现内存错误,这样如果在一个模块分配的内存在另外一个模块释放就会出现内存错误。解决这个问题就需要使用到共享内存管理器,让各个模块都使用同一个内存管理器。Delphi缺省的共享内存管理器是BORLNDMM.DLL,这个内存管理器并不可靠,也常常出现问题,并且,在程序发布的时候必须连同这个DLL一起发布。而FastMM的共享内存管理器功能不需要DLL支持,并且更加可靠。
    第三个原因是FastMM还拥有一些帮助程序开发的辅助功能,如内存泄漏检测功能,可以检测程序是否存在未正确释放的内存等。

    三、出现什么问题
    如果我们开发的应用程序,只有一个exe模块,那么,使用FastMM是一件非常简单的事情,只需要把FastMM.pas(最新版是FastMM4.pas)作为工程文件的第一个uses单元即可,如:

    program Test;
    uses
        FastMM4,
        …
    但是,通常情况下,我们的应用程序都是由一个exe模块加上多个dll组成的,这样,当我们跨模块传递动态内存变量如string变量时,就会出问题,比如,下面的测试程序由一个exe和一个dll组成:

    library test;   // test.dll
    uses
        FastMM4, …;
    procedure GetStr(var S: string; const Len: Integer); stdcall;
    begin
        SetLength(S, Len); // 分配内存
        FillChar(S[1], Len, ‘A’);  
    end;
    exports
        GetStr;
    ————————————————————————————————————-
    program TestPrj;
    uses
        FastMM4, …;
    //——————————————————
    unit mMain; // 测试界面

    procedure TForm1.btnDoClick(Sender: TObject);
    var
    I: Integer;
    S: string;
    Begin
    try
    for I := 1 to 10000 do
    begin
        GetStr(S, I + 1);
        edt1.Text := S;
        Application.ProcessMessages;
    end;
    finally
        SetLength(S, 0);
    end;
    end;

    当第二次执行btnDoClick过程时,就会出现内存错误,为什么这样?delphi的字符串是带引用计数的,跟接口变量一样,一旦这个引用计数为0,则会自动释放内存。在btnDoClick过程中,调用GetStr过程,用SetLength给S分配了一段内存,此时这个字符串的引用计数为1,然后执行edt1.Text := S语句,字符串的引用计数为2,循环再调用GetStr给S重新分配内存,这样原来的字符串的引用计数减1,再执行edt1.Text := S,原来的字符串引用计数为0,这时,就会被释放(注意,是在TestPrj.exe释放,而不是在Test.dll释放),但这时没有出错,当循环执行完毕之后,还有一个字符串的引用计数为2,但是执行SetLength(S, 0)之后,该字符串被edt1.Text引用,的引用计数为1,第二次执行btnDoClick时,执行edt1.Text := S时,上次的引用计数为1的字符串引用计数减一变为0,就会被释放,此时,会出现内存错误。
    由此,可以看到,在另一个模块释放别的模块分配的内存,并不一定马上出现内存错误,但是,如果频繁执行,则会出现内存错误,这种不确定的错误带有很大的隐蔽性,常常在调试时不出现,但实际应用时出现,不仔细分析很难找到原因。
    要解决这个问题,就要从根源找起,这个根源就是内存管理。
    一、Delphi的内存管理
    Delphi应用程序可以使用的有三种内存区:全局内存区、堆、栈,全局内存区存储全局变量、栈用来传递参数以及返回值,以及函数内的临时变量,这两种都是由编译器自动管理,而如字符串、对象、动态数组等都是从堆中分配的,内存管理就是指对堆内存管理,即从堆中分配内存和释放从堆中分配的内存(以下称内存的分配和释放)。
    我们知道,一个进程只有一个栈,因此,也很容易误以为一个进程也只有一个堆,但实际上,一个进程除了拥有一个系统分配的默认堆(默认大小1MB),还可以创建多个用户堆,每个堆都有自己的句柄,delphi的内存管理所管理的正是自行创建的堆,delphi还把一个堆以链表的形式分成多个大小不等的块,实际的内存操作都是在这些块上。
    delphi把内存管理定义为内存的分配(Get)、释放(Free)和重新分配(Realloc)。内存管理器也就是这三种实现的一个组合,delphi在system单元中定义了这个内存管理器TMemoryManager:

    PMemoryManager = ^TMemoryManager;
    TMemoryManager = record
        GetMem: function (Size: Integer): Pointer;
        FreeMem: function (P: Pointer): Integer;
        ReallocMem: function (P: Pointer; Size: Integer): Pointer;
    end;
    由此知道,delphi的内存管理器就是一个 TMemoryManager 记录对象,该记录有三个域,分别指向内存的分配、释放和重新分配例程。
    System单元还定义了一个变量 MemoryManager :
    MemoryManager: TMemoryManager = (
        GetMem: SysGetMem;
        FreeMem: SysFreeMem;
        ReallocMem: SysReallocMem);
    该变量是delphi程序的内存管理器,缺省情况下,这个内存管理器的三个域分别指向GETMEM.INC中实现的SysGetMem、SysFreeMem、SysReallocMem。这个内存管理器变量只在system.pas中可见,但是system单元提供了三个可以访问该变量的例程:

    // 读取内存管理器,也即读取MemoryManager
    procedure GetMemoryManager (var MemMgr: TMemoryManager);
    // 安装内存管理器(即用新的内存管理器替换缺省的内存管理器)
    procedure SetMemoryManager (const MemMgr: TMemoryManager);
    // 是否已经安装了内存管理器(即缺省的内存管理器是否已经被替换)
    function IsMemoryManagerSet: Boolean;

    四、共享内存管理器
    什么是共享内存管理器?
    所谓共享内存管理器,就是一个应用程序的所有的模块,不管是exe还是dll,都使用同一个内存管理器来管理内存,这样,内存的分配和释放都是同一个内存管理器完成的,就不会出现内存错误的问题。
    那么如何共享内存管理器呢?
    由上分析,我们可以知道,既然要使用同一个内存管理器,那么干脆就创建一个独立的内存管理器模块(dll),其他的所有模块都使用这个模块的内存管理器来分配和释放内存。Delphi7默认就是采取这种方法,当我们使用向导创建一个dll工程时,工程文件会有这样一段话:
    {Important note about DLL memory management: ShareMem must be the
    first unit in your library's USES clause AND your project's (select
    Project-View Source) USES clause if your DLL exports any procedures or
    functions that pass strings as parameters or function results. This
    applies to all strings passed to and from your DLL——even those that
    are nested in records and classes. ShareMem is the interface unit to
    the BORLNDMM.DLL shared memory manager, which must be deployed along
    with your DLL. To avoid using BORLNDMM.DLL, pass string information
    using PChar or ShortString parameters. }
    这段话提示我们,ShareMem 是 BORLNDMM.DLL 共享内存管理器的接口单元,我们来看看这个ShareMem,这个单元文件很简短,其中有这样的声明:

    const
    DelphiMM = 'borlndmm.dll';
    function SysGetMem (Size: Integer): Pointer;
    external DelphiMM name '@Borlndmm@SysGetMem$qqri';
    function SysFreeMem(P: Pointer): Integer;
    external DelphiMM name '@Borlndmm@SysFreeMem$qqrpv';
    function SysReallocMem(P: Pointer; Size: Integer): Pointer;
    external DelphiMM name '@Borlndmm@SysReallocMem$qqrpvi';
    这些声明保证了BORLNDMM.DLL将被静态加载。
    在ShareMem的Initialization是这样的代码:
    if not IsMemoryManagerSet then
        InitMemoryManager;
    首先判断内存管理器是否已经被安装(也即是否默认的内存管理器被替换掉),如果没有,则初始化内存管理器,InitMemoryManager也非常简单(把无用的代码去掉了):

    procedure InitMemoryManager;
    var
    SharedMemoryManager: TMemoryManager;
    MM: Integer;
    begin
    // force a static reference to borlndmm.dll, so we don't have to LoadLibrary
    SharedMemoryManager.GetMem:= SysGetMem;
    MM: = GetModuleHandle (DelphiMM);
    SharedMemoryManager.GetMem:= GetProcAddress (MM,'@Borlndmm@SysGetMem$qqri');
    SharedMemoryManager.FreeMem:= GetProcAddress (MM,'@Borlndmm@SysFreeMem$qqrpv');
    SharedMemoryManager.ReallocMem:= GetProcAddress (MM, '@Borlndmm@SysReallocMem$qqrpvi');
    SetMemoryManager (SharedMemoryManager);
    end;
    这个函数定义了一个内存管理器对象,并设置域指向Borlndmm.dll的三个函数实现,然后调用SetMemoryManager来替换默认的内存管理器。
    这样,不管那个模块,因为都要将ShareMem作为工程的第一个uses单元,因此,每个模块的ShareMem的Initialization都是最先被执行的,也就是说,每个模块的内存管理器对象虽然不相同,但是,内存管理器的三个函数指针都是指向Borlndmm.dll的函数实现,因此,所有模块的内存分配和释放都是在Borlndmm.dll内部完成的,这样就不会出现跨模块释放内存导致错误的问题。
    那么,FastMM又是如何实现共享内存管理器呢?
    FastMM采取了一个原理上很简单的办法,就是创建一个内存管理器,然后将这个内存管理器的地址放到一个进程内所有模块都能读取到的位置,这样,其他模块在创建内存管理器之前,先查查是否有别的模块已经把内存管理器放到这个位置,如果是则使用这个内存管理器,否则才创建一个新的内存管理器,并将地址放到这个位置,这样,这个进程的所有模块都使用一个内存管理器,实现了内存管理器的共享。
    而且,这个内存管理器并不确定是哪个模块创建的,所有的模块,只要将FastMM作为其工程文件的第一个uses单元,就有可能是这个内存管理器的创建者,关键是看其在应用程序的加载顺序,第一个被加载的模块将成为内存管理器的创建者。
    那么,FastMM具体是如何实现的呢?
    打开 FastMM4.pas(FastMM的最新版本),还是看看其Initialization部分:

    {Initialize all the lookup tables, etc. for the memory manager}
    InitializeMemoryManager;
    {Has another MM been set, or has the Borland MM been used? If so, this file
       is not the first unit in the uses clause of the project's .dpr file.}
    if CheckCanInstallMemoryManager then
    begin
        InstallMemoryManager;
    end;
    InitializeMemoryManager 是初始化一些变量,完成之后就调用CheckCanInstallMemoryManager检测FastMM是否是作为工程的第一个uses单元,如果返回True,则调用InstallMemoryManager安装FastMM自己的内存管理器,我们按顺序摘取该函数的关键性代码进行分析:
    {Build a string identifying the current process}
    LCurrentProcessID: = GetCurrentProcessId;
    for i := 0 to 7 do
    UniqueProcessIDString [8 - i]:= HexTable [((LCurrentProcessID shr (i * 4)) and $F)];
    MMWindow: = FindWindow ('STATIC', PChar (@UniqueProcessIDString [1]));
    首先,获取该进程的ID,并转换为十六进制字符串,然后查找以该字符串为窗口名称的窗口。
    如果进程中还没有该窗口,则MMWindow 将返回0,那就,就创建该窗口:
    MMWindow: = CreateWindow ('STATIC', PChar (@UniqueProcessIDString [1]),
              WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
    创建这个窗口有什么用呢,继续看下面的代码:

    if MMWindow <> 0 then
    SetWindowLong (MMWindow, GWL_USERDATA, Integer (@NewMemoryManager));
    NewMemoryManager.Getmem: = FastGetMem;
    NewMemoryManager.FreeMem: = FastFreeMem;
    NewMemoryManager.ReallocMem: = FastReallocMem;
    查阅MSDN可以知道,每个窗口都有一个可供创建它的应用程序使用的32位的值,该值可以通过以GWL_USERDATA为参数调用SetWindowLong来进行设置,也可以通过以GWL_USERDATA为参数调用GetWindowLong来读取。由此,我们就很清楚地知道,原来FastMM把要共享的内存管理器的地址保存到这个值上,这样其他模块就可以通过GetWindowLong来获读取到这个值,从而得到共享的内存管理器:

    NewMemoryManager: = PMemoryManager (GetWindowLong (MMWindow, GWL_USERDATA)) ^;
    通过上面的分析,可以看出,FastMM 在实现共享内存管理器上要比borland巧妙得多,borland的实现方法使得应用程序必须将BORLNDMM.DLL一起发布,而FastMM的实现方法不需要任何dll的支持。
    但是,上面的摘录代码把一些编译选项判断忽略掉了,实际上,要使用FastMM的共享内存管理器功能,需要在各个模块编译的时候在FastMM4.pas单元上打开一些编译选项:
    {$define ShareMM} //是打开共享内存管理器功能,是其他两个编译选项生效的前提
    {$define ShareMMIfLibrary} //是允许一个dll共享其内存管理器,如果没有定义这个选项,则一个应用程序中,只有exe模块才能够创建和共享其内存管理器,由于静态加载的dll总是比exe更早被加载,因此,如果一个dll会被静态加载,则必须打开该选项,否则可能会出错
    {$define AttemptToUseSharedMM} //是允许一个模块使用别的模块共享的内存管理器
    这些编译选项在FastMM4.pas所在目录的FastMM4Options.inc文件中都有定义和说明,只不过这些定义都被注释掉了,因此,可以取消注释来打开这些编译选项,或者可以在你的工程目录下创建一个.inc文件(如FastShareMM.inc),把这些编译选项写入这个文件中,然后,在FastMM4.pas开头的“{$Include FastMM4Options.inc}”之前加入“{$include FastShareMM.inc}”即可,这样,不同的工程可以使用不同的FastShareMM.inc文件。

    五、多线程下的内存管理
    多线程环境下,内存管理是安全的吗?显然,如果不采取一定的措施,那么肯定是不安全的,borland已经考虑到这种情况,因此,在delphi的system.pas中定义了一个系统变量IsMultiThread,这个系统变量指示当前是否为多线程环境,那么,它是如何工作的?打开TThread.Create函数的代码可以看到它调用了BeginThread来创建一个线程,而BeginThread把IsMultiThread设置为了True.
    再来看看GETMEM.INC的SysGetMem、SysFreeMem、SysReallocMem的实现,可以看到,在开始都由这样的语句:
    if IsMultiThread then EnterCriticalSection(heapLock);
    也就是说,在多线程环境下,内存的分配和释放都要用临界区进行同步以保证安全。
    而FastMM则使用了一条CUP指令lock来实现同步,该指令作为其他指令的前缀,可以在在一条指令的执行过程中将总线锁住,当然,也是在IsMultiThread为True的情况下才会进行同步。
    而IsMultiThread是定义在system.pas的系统变量,每个模块(exe或者dll)都有自己的IsMultiThread变量,并且,默认为Fasle,只有该模块中创建了用户线程,才会把这个变量设置为True,因此,我们在exe中创建线程,只会把exe中的IsMultiThread设置为True,并不会把其他的dll模块中的IsMultiThread设置为True,但是,前面已经说过,如果我们使用了静态加载的dll,这些dll将会比exe更早被系统加载,这时,第一个被加载的dll就会创建一个内存管理器并共享出来,其他模块都会使用这个内存管理器,也就是说,exe的IsMultiThread变量没有影响到应用程序的内存管理器,内存管理器还是认为当前不是多线程环境,因此,没有进行同步,这样就会出现内存错误的情况。
    解决这个问题就是要想办法当处于多线程环境时,让所有的模块的IsMultiThread都设置为True,以保证不管哪个模块实际创建了内存管理器,该内存管理器都知道当前是多线程环境,需要进行同步处理。
    还好,windows提供了一个机制,可以让我们的dll知道应用程序创建了线程。DllMain函数是dll动态链接库的入口函数,delphi把这个入口函数封装起来,提供了一个TDllProc的函数类型和一个该类型的变量DllProc:

    TDLLProc = procedure (Reason: Integer); // 定义在system.pas
    // 定义在sysinit.pas:
    var
        DllProc: TDllProc;

    当系统调用dll的DllMain时,delphi最后会调用DllProc进行处理,DllProc可以被指向我们自己的TDllProc实现。而当进程创建了一个新线程时,操作系统会以Reason=DLL_THREAD_ATTACH为参数调用DllMain,那么delphi最后会以该参数调用DllProc,因此我们只要实现一个新的TDllProc实现ThisDllProc,并将DllProc指向ThisDllProc,而在ThisDllProc中,当收到DLL_THREAD_ATTACH时把IsMultiThread设置为True即可。实现代码如下:

    library xxx;
    var
    OldDllProc: TDLLProc;
    procedure ThisDllProc(Reason: Integer);
    begin
    if Reason = DLL_THREAD_ATTACH then
        IsMultiThread := True;
    if Assigned(OldDllProc) then
        OldDllProc(Reason);
    end;
    begin
    OldDllProc := DllProc;
    DllProc := ThisDllProc;
    ThisDllProc(DLL_PROCESS_ATTACH);


    六、总结
    本文主要讨论了下面几个问题:
        1、为什么要使用FastMM
        2、跨模块传递动态内存变量会出现什么问题,原因是什么
        3、delphi的内存管理和内存管理器是怎么回事
         4、为什么要共享内存管理器,delphi和FastMM分别是如何实现内存管理器共享的
         5、多线程环境下,内存管理器如何实现同步
         6、多线程环境下,如何跨模块设置IsMultiThread变量以保证内存管理器会进行同步

    要正确使用FastMM,在模块开发的时候需要完成以下工作:
        1、打开编译选项{$define ShareMM}、{$define ShareMMIfLibrary}、{$define AttemptToUseSharedMM}
        2、将FastMM(4).pas作为每个工程文件的第一个uses单元
        3、如果是dll,需要处理以DLL_THREAD_ATTACH为参数的DllMain调用,设置IsMultiThread为True

    七、参考文献
    《Windows 程序设计第五版》[美]Charles Petzold著,北京大学出版社
    《Delphi源代码分析》 周爱民 著,电子工业出版社

    展开全文
  • procedure TForm1.Button1Click(Sender: TObject); {设置键盘钩子} begin EnableKeyHook; //设置钩子 Button1.Enabled:=False; Button2.Enabled:=True; Button3.Enabled:=True;... button4.Enabled:=True;...
  • 动态调用DLL并与主程序通信,内存共享,线程安全
  • ShareMemLib将共享内存代码封装成lib,定义了发送者和监听者 两个进程在实例化ShareMemory时指定相同的map,并指定当前为发送者或监听者 发送者负责发送消息,监听者负责接受消息(监听者务必实现接受消息处理方法) ...
  • 代码已经封装为C++类的方式。内存共享分为服务端和客户端,服务端写入数据,客户端读取数据,服务端和客户端通过使用相同的名字,来确认配对。 下载只需要1积分,如果发现多于1积分,请留言我重新上传资源。
  • 内存中运行exe文件 不写出 不释放 支持dll 对于加壳的软件可能不是很理想 自己测试下吧 易语言在内存运行EXE何DLL
  • 一旦DLL的文件映像被映射到调用进程的地址空间中,DLL的函数就可以供进程中运行的所有线程使用。实际上,DLL几乎将失去它作为DLL的全部特征。对于进程中的线程来说,DLL的代码和数据看上去就像恰巧是在进程的地址...

    1.       无论这些第三方库是静态库还是动态库,或者同时有静态库和动态库(可以同时使用),必须保证在生成这些库时,使用的C Runtime库是同一个版本(/MT, /MTd, /MD,或者/MDd)【如果是VC的话,在项目属性->配置属性->C/C++->Code Generate->Runtime Library中设置】。 否则,在编译可执行文件时,会出现重复定义的问题,这是因为C Runtime库的不同版本之间的冲突造成的。所以,如果你要提供一个第三方库给别人使用,最好提供使用了不同C Runtime库的多个版本(/MT, /MTd, /MD,或者/MDd),以供选择。我推荐使用/MD和/MDd,原因下面第4节中讲。

     

    2.       一旦DLL的文件映像被映射到调用进程的地址空间中,DLL的函数就可以供进程中运行的所有线程使用。实际上,DLL几乎将失去它作为DLL的全部特征。 对于进程中的线程来说,DLL的代码和数据看上去就像恰巧是在进程的地址空间中的额外代码和数据一样。当一个线程调用DLL函数时,该DLL函数要查看线程的堆栈,以便检索它传递的参数,并将线程的堆栈用于它需要的任何局部变量。此外, DLL中函数的代码创建的任何对象均由调用线程所拥有,而DLL本身从来不拥有任何东西。 

      如你所知,可执行文件的全局变量和静态变量不能被同一个可执行文件的多个运行实例共享。Windows98能够确保这一点,方法是在可执行文件被映射到进程的地址空间时为可执行文件的全局变量和静态变量分配相应的存储器。Windows2000确保这一点的方法是使用第13章介绍的写入时拷贝 (copy-on-write)机制。DLL中的全局变量和静态变量的处理方法是完全相同的。当一个进程将DLL的映像文件映射到它的地址空间中去时,系统将同时创建全局数据变量和静态数据变量的实例

    必须注意的是,单个地址空间是由一个可执行模块和若干个DLL模块组成的。这些模块中,有些可以链接到静态版本的C/C++运行期库,有些可以链接到一个DLL版本的C/C++运行期库,而有些模块(如果不是用C/C++编写
    的话)则根本不需要C/C++运行期库。许多开发人员经常会犯一个常见的错误,因为他们忘记了若干个C/C++运行期库可以存在于单个地址空间中。请看下面的代码:  

    VOID   EXEFunc(){  PVOID pv = DLLFunc();  free(pv);}  
    PVOID  DLLFunc() {  return(malloc(100)); }

      那么你是怎么看待这个问题的呢?上面这个代码能够正确运行吗?DLL函数分配的内存块是由EXE的函数释放的吗?答案是可能的。上面显示的代码并没有 为你提供足够的信息。如果EXE和DLL都链接到DLL的C/C++运行时库(/MD),那么上面的代码将能够很好地运行。  但是,如果两个模块中的一个或者两个都链接到静态C/C++运行期库(/MT),那么对free函数的调用就会失败(原因下面第三节解释)。 我经常看到编程人员编写这样的代码,结果都失败了。

      有一个很方便的方法可以解决这个问题。当一个模块提供一个用于分配内存块的函数时,该模块也必须提供释放内存的函数。让我们将上面的代码改写成下面的样子:  
    VOID   EXEFunc(){   PVOID   pv   =   DLLFunc();   DLLFreeFunc(pv);}
    PVOID   DLLFunc(){   PVOID   pv   =   malloc(100);   return(pv);}  
    BOOL   DLLFreeFunc(PVOID   pv){   return(free(pv));}  

      上面这个代码是正确的,它始终都能正确地运行。当你编写一个模块时,不要忘记其他模块中的函数也许没有使用C/C++来编写,因此可能无法使用malloc和free函数进行内存的分配。应该注意不要在代码中使用这些假设条件。

    另外,在内部调用malloc和free函数时,这个原则对于C++的new和delete操作符也是适用的。

    3.       问题:
    1.“如果两个模块中的一个或者两个都链接到静态C/C++运行期库,那么对free函数的调用就会失败”,为什么?   静态CRT library和动态CRT library有什么区别?

    答:因为malloc/free, new/delete   都是调用HeapAlloc/HeapFree   来实现来实现内存分配是释放的。查看Windows的API可以看到,这两个函数都需要一个Heap的HANDLE做为参数。CRT   库采用了全局变量来保存这个HANDLE。如果是静态链接, CRT库的代码会链接到各个dll中去,也包括这个全局变量。也就是说,每个静态链接的dll都有一个自己的全局堆句柄,他们自己都在这个句柄上使用内存。当一个dll释放另外一个dll分配的内存时由于使用的堆句柄不一致于是出错。当使用动态链接时,有于每个dll 都是去调用CRT库的dll函数来分配和释放内存的,使用的是同一个句柄,所以就没有这个问题。
    2.   “DLL中函数的代码创建的任何对象均由调用线程所拥有,而DLL本身从来不拥有任何东西”, DLL中的全局变量怎么解释?由多个应用程序使用该DLL时,DLL中的全局变量需要加上同步机制吗?

    答:不同进程之间对   dll   全局变量的访问操作系统可以区分的,平时大家共用一份,当某个进程改了某个全局变量时,操作系统会自动为这个进程生成一份这个变量的拷贝,(copy- on-write)让这个进程访问拷贝的内容而不会影响其它进程中这个变量的值,所以不需要手动做同步。

     

    4.       总结

    综上所述,要在同一个DLL中对变量进行内存分配和释放的根本原因在于:每个DLL都要保存一份全局变量和静态变量的拷贝;CRT库中将堆的句柄保存为一个全局变量,而堆中内存的分配和释放都要使用这个句柄。

    如果可执行程序和DLL都使用动态链接的CRT库(/MD,/MDd),那么CRT库只初始化一次(在其DLL中),每次内存分配和释放也只在CRT的DLL库中进行,这样使用唯一一个堆的句柄,就不会导致错误。

    如果有一个DLL(称为D1)使用静态链接的CRT库(/MT,/MTd),可执行程序也使用静态链接的,那么D1中有一个堆的句柄,可执行程序中也有一个堆的句柄,两者进行内存分配和释放时使用各自的堆的句柄。那么就必须保证使用D1分配的内存,必须由D1释放,否则就会出错。这也就是1中我推荐使用/MD的原因。

     

     

    //文章被翻来覆去的。。。

    //上面可能是原文,下面可能是转载人加上的,不过感谢他们的劳动成果,

    //拿下来慢慢看~

    1.因为   malloc/free,   new/delete   都是调用   HeapAlloc/HeapFree   来实现来实现内存分配是释放的。查看   Windows   的   API   可以看到,这两个函数都需要一个   Heap   的   HANDLE   做为参数。CRT   库采用了全局变量来保存这个   HANDLE。如果是静态链接,   CRT   库的代码会链接到各个   dll   中去,也包括这个全局变量。也就是说,每个静态链接的   dll   都有一个自己的全局堆句柄,他们自己都在这个句柄上使用内存。当一个   dll   释放另外一个   dll   分配的内存时由于使用的堆句柄不一致于是出错。当使用动态链接时,有于每个   dll   都是去调用   CRT   库的   dll   函数来分配和释放内存的,使用的是同一个句柄,所以就没有这个问题。

    2.   不同进程之间对   dll   全局变量的访问操作系统可以区分的,平时大家共用一份,当某个进程改了某个全局变量时,操作系统会自动为这个进程生成一份这个变量的拷贝,(copy- on-write)让这个进程访问拷贝的内容而不会影响其它进程中这个变量的值,所以不需要手动做同步。

     

    在网上找到一段文字,再结合adlay的解释基本上清楚了.
    http://dev.csdn.net/author/houdy/9f4bb2dc376d437787032971ee0eff97.html
    四.malloc/free
      这两个函数是使用频率最高的两个函数,由于他们是标准C库中的一部分,所以具有极高的移植性。这里的"移植性"指的是使用他们的代码可以在不同的平台下编译通过,而不同的平台下的C Run-Time Library的具体实现是平台相关的,在Windows平台的C Run-Time Library中的malloc()和free()是通过调用Heap Memory API来实现的。值得注意的是C Run-Time Library拥有独立的Heap对象,我们知道,当一个应用程序初始化的时候,首先被初始化的是C Run-Time Library,然后才是应用程序的入口函数,而Heap对象就是在C Run-Time Library被初始化的时候被创建的。对于动态链接的C Run-Time Library,运行库只被初始化一次,而对于静态连接的运行库,每链接一次就初始化一次,所以对于每个静态链接的运行库都拥有彼此不同的Heap 对象。这样在某种情况下就会出问题,导致程序崩溃,例如一个应用程序调用了多个DLL,除了一个DLL外,其他的DLL,包括应用程序本身动态连接运行库,这样他们就使用同一个Heap对象。而有一个DLL使用静态连接的运行库,它就拥有一个和其他DLL不同的Heap 对象,当在其他DLL中分配的内存在这个DLL中释放时,问题就出现了。

    展开全文
  • Delphi多个DLL共享全局数据Demo

    热门讨论 2012-03-20 11:46:55
    Delphi多个DLL共享全局数据Demo
  • 该例程实现C#与Codesys通过共享内存的方式完成数据交互,这样有利于垮软件平台之间进行通讯,相较于其他通信方式(Modbus、TCP/IP等)更加方便快捷。
  • BPL和DLL中Form共享数据库连接和模块调用的例子.rar
  • vs2010下使用CAtlFileMapping实现的共享内存dll,并使用opencv获取和显示图像,一个图像获取程序(也显示图像和另一个程序对比),一个图像显示程序,进程进行了简单的同步,是简单的例子。
  • 在写 E4W 的时候遇到内存shuj共享的问题,苦恼了好几天,网上好像,应该是没有这个解决方案?(可能是我眼拙) 所以算不算全网首发了? 对了!线程安全。稳稳的! demo1: demo2:
  • 共享 DLL 中使用 MFC 出现内存泄漏

    千次阅读 2015-04-28 10:06:04
    写了个lib库程序。 在控制台应用程序中调用该lib库,执行都正常,退出时也没有内存泄漏。 在MFC界面应用程序中调用该lib库,执行...[在共享 DLL 中使用 MFC] 提示内存泄漏 设置为[在静态库中使用 MFC]后,提示消失。

    写了个lib库程序。

    在控制台应用程序中调用该lib库,执行都正常,退出时也没有内存泄漏。

    在MFC界面应用程序中调用该lib库,执行正常,但退出时报告有内存泄漏。

    如下:

    Detected memory leaks!
    Dumping objects ->
    {168} normal block at 0x0080FF18, 16 bytes long.
     Data: <   a        `   > DC D9 C8 61 02 00 00 00 01 00 00 00 60 FE 80 00
    {167} normal block at 0x0080FEC8, 14 bytes long.
     Data: <bad exception > 62 61 64 20 65 78 63 65 70 74 69 6F 6E 00
    {166} normal block at 0x0080FE60, 44 bytes long.
     Data: <   a    (  a   a> EC B9 C8 61 00 00 00 00 28 BA C8 61 E0 B8 C8 61
    {163} normal block at 0x0080FD70, 16 bytes long.
     Data: <   a        @   > C0 D9 C8 61 02 00 00 00 01 00 00 00 40 F7 80 00
    {162} normal block at 0x0080F740, 44 bytes long.
     Data: <   a    @  a   a> A4 B8 C8 61 00 00 00 00 40 B9 C8 61 E0 B8 C8 61
    {161} normal block at 0x0080F6F8, 8 bytes long.
     Data: <$  a    > 24 D1 C9 61 00 00 00 00
    Object dump complete.

    一遍又一遍的检查了代码,没有发现程序上有内存泄漏的地方。

    经过调查,MFC的使用改为[在静态库中使用 MFC]后,重新编译运行,检测出内存泄漏的提示消失。

    我写的程序中有全局变量类实例,而且里面new了内存,但类实例析构时delete了内存。

    猜测有可能是全局变量释放前进行了内存泄漏检测。导致出现内存泄漏提示。

    设置为[在静态库中使用 MFC]后,内存泄漏的检测置后到全局变量释放后了,所以就没有问题了。

    在MFC中使用ACE也出现了类似的问题。估计采用同样方法都可以解决。


    
    展开全文
  • c#调用c++dll共享内存需要函数

    千次阅读 2012-05-03 10:34:31
    调用函数如下,都是项目中用到的函数,这样用到的时候不需要再照着msdn中c++函数一... [DllImport("kernel32.dll", EntryPoint = "CreateFileMapping", SetLastError = true, CharSet = CharSet.Ansi)] public static
  • FastMM是Embarcadero Delphi应用程序的快速替换内存管理器,可在多个线程和CPU内核之间很好地扩展,不容易出现内存碎片,并且无需使用外部.DLL文件即可支持共享内存。 版本5是对FastMM的完全重写。它是从头开始设计...
  • 关于使用MFC DLL时提示内存泄漏问题

    千次阅读 2017-08-22 10:41:18
    这两天按照之前的习惯,在开发过程中使用调试模式运行的时候,发现有意料之外的内存泄漏问题。经过不断的核查定位,最终发现是在使用FreeLibrary函数卸载自己写的一个MFC ...然后找了一下调用MFC DLL内存泄漏相关问题
  • 1.基于QT的进程间通信,利用共享内存进行图片(Mat格式)传输。调用库opencv。 2.分为两个进程,进程ProA和进程ProB。 3.代码简单少量。下载即可运行。
  • DLL载入内存的问题

    千次阅读 2018-11-07 10:41:58
    在Win32环境下,所有进程的空间都是相互独立的,这减少了应用程序间的相互影响,但大大增加了编程...当DLL内存被映射到进程空间中,每个进程都有自己的全局内存拷贝,加载DLL的每一个新的进程都重新初始化这一内存区...
  •   简单的DLL /*---------------------- EDRLIB.H header file ----------------------*/ #ifdef __cplusplus #define EXPORT extern "C" __declspec (dllexport) #else #define EXPORT __...
  • 一、概述 很多情况下在Windows程序中,各个进程之间...例如:如果两个进程使用相同的DLL,只把DLL的代码页装入内存一次,其他所有映射这个DLL的进程只要共享这些代码页就可以了;利用消息机制实现IPC虽然有交换的数据
  • 设置dll共享数据段

    2014-06-17 08:39:57
    BCB直接在DLL中定义共享内存单元的步骤如下:  假设DLL的工程名为MouseHook.bpr(编译后产生MouseHook.DLL)  1.在一程序模块 Unit1.cpp的最前面加上:  #pragma option -zRSHSEG // 改变缺省数据段名  #...
  • dll中的内存管理

    2018-03-08 16:44:28
    C++编写可供C#调用的算法模块,算法有实时性要求,而且涉及大量的处理数据(摄像头采集的多帧大...如动态内存的管理,不要产生野指针低耦合的dll封装性,内部变量、与调用功能无关的变量不要暴露给调用方,有利...
  • DLL内存分配与共享

    千次阅读 2012-02-25 11:02:24
    一旦DLL的文件映像被映射到调用进程的地址空间中,DLL的函数就可以供进程中运行的所有线程使用。实际上,DLL几乎将失去它作为DLL的全部特征。对于进程中的线程来说,DLL的代码和数据看上去就像恰巧是在进程的地址...
  • 共享内存编程

    2014-03-07 11:31:46
    共享内存用例,可将该代码打包成DLL,与我之前上传的AS/400异步短连接接收工具集LoadRunner配合使用。用于测试异步通信系统。将ID号及发送报文时间等信息使用DLL加载到共享内存,异步接收工具负责接收返回报文,收到...
  • 显示调用共享内存dll

    千次阅读 2014-09-25 15:46:21
    隐式调用dll:VS2008创建DLL,并且使用DLL

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 62,311
精华内容 24,924
关键字:

dll内存共享