======关于动态链接库DLL动态调用资源释放问题(每次调用都有4~12K的内存泄漏),如何解决!======(200分)

  • 主题发起人 主题发起人 saellen
  • 开始时间 开始时间
查看一下,您一定在某个地方内存没释放,
......
new(a)
......
dispose(a)

.........

查看一下,new和 dispose是否配套.
 
如果在调用Dll的主窗口中使用了如LoadBitmap等函数,请务必在程序退出时使用FreeObject
函数释放使用LoadBitmap加载的句柄。

另外,不要使用加壳工具压缩Dll.
 
是不是就是那个MakeObjectInstance 跟 FreeObjectInstance造成的Bug?
 
类似的Load*或Create*等函数都需要使用对应的Free*函数释放句柄。具体函数,请查看
帮助。里面说的很清楚。

不过还又一个问题,我有一个专门用来检测EXE文件内存漏洞的.pas文件,可是我每次
只用Delphi编译一个空程序,也会报告内存漏洞。那么不排除Delphi自己的Bug.
 
大家可能把我的意思理解错了,我是每次调用DLL ,释放DLL以后的,
在WIN2K发现,每次都有4~12K的内存泄漏),就如何解决.调用方法如下.
Function ....:boolean;
var Ahandle: Thandle;
FPointer: TFarProc;
MyFunc: TMakeZIPFile;
begin
Result := False;
try
Ahandle := LoadLibrary(Pchar(DllName));//DLLName为DLL名称.
if Ahandle <> 0 then //如果成功
begin
try
FPointer := GetProcAddress(Ahandle, PChar('MakeZIPFile')); //获得函数地址
if FPointer <> nil then
begin
MyFunc := TMakeZIPFile(FPointer);
Result := MyFunc(FileName, ZIPFile, Pwd);
end
finally
FreeLibrary(Ahandle);
end;
end
except
end;
end;
 
这样修改:
Function ....:boolean;
var Ahandle: Thandle;
//FPointer: TFarProc;
MyFunc: TMakeZIPFile;
begin
Result := False;
try
Ahandle := LoadLibrary(Pchar(DllName));//DLLName为DLL名称.
if Ahandle <> 0 then //如果成功
begin
try
{FPointer} @MyFunc:= GetProcAddress(Ahandle, PChar('MakeZIPFile')); //获得函数地址
if {FPointer} @MyFunc<> nil then
begin
//MyFunc := TMakeZIPFile(FPointer);
Result := MyFunc(FileName, ZIPFile, Pwd);
end
finally
FreeLibrary(Ahand);
end;
end
except
end;
end;
 
To zqw0117:
谢谢你的回答,可惜和我的程序效果是一样的,还是没办法把资源释放掉!
 
据我所知,这是Delphi的BUG,解决方法就是更正我所说的
MakeObjectInstance 跟 FreeObjectInstance方法,虽然你没有调用这两个方法:)
你试试看用下面的代码替换Forms.pas单元中的相应方法。
// Replacement for MakeObjectInstance and FreeObjectInstance

// This is the maximum number of object instances that can be
// put in one memory page, with other reference data.
Const InstanceCount = 312;

// The following 4 types are used for double linked lists of instance
// objects and blocks.
Type PObjectInstance = ^TObjectInstance;
TObjectInstance = Packed Record
Code :Byte;
Offset :Integer;
Case Integer Of
0 :(Next, Prev :PObjectInstance);
1 :(Method :TWndMethod);
End; // 13 bytes in size. Note that it's packed!
// The size matters becuase we have to know how many of these records
// can be put inside one Instance Block (see below), for the block to
// be as close as possible to one memory page size (but less than it!).

Type PInstanceBlock = ^TInstanceBlock;
TInstanceBlock = Packed Record
Next :PInstanceBlock; // Points to the previous Instance block
Code :Array[1..2] Of Byte; // Instruction bytes.
WndProcPtr :Pointer; // Window Procedure Pointer
InstCount :Integer; // How many object instances are used in the block
Prev :PInstanceBlock; // Points to the previous Instance block
// Used for faster list management.
Instances :Array[0..InstanceCount] Of TObjectInstance;
// ^ Instance objects.
End; // Size of the block is:
// 4 bytes for Next
// 2 bytes for Code
// 4 bytes for WndProcPtr
// 4 bytes for InstCount
// 4 bytes for Prev
// 312 * 13 bytes for Instances
// -----------------------------
// 18 + 313 * 13 = 18 + 4069 = 4807 bytes. Just 9 bytes less than
// a memory page:-)

Var InstBlockList :PInstanceBlock = Nil; // The list of blocks currently allocated
InstFreeList :PObjectInstance = Nil; // The list of free Object instances
// The below variables are only used if you need synchronization of the objects
// I suggest that you do not use these! It will only make a small
// overhead in the code, and due to the way VCL is made, it is not needed
{$IFDEF VCL_Synchronize}
ObjInstLock :TRTLCriticalSection; // Critical section used to guard the memory
ObjInstInit :Boolean = False; // Has the critical section been initialized?
{$ENDIF}

// CAUTION: There are three possible problems in the below proceudre:
// 1 :) Synchronization is not enabled by default (according to Borland
// the data is not intended to be thread safe, and I think that's OK, more
// or less, if you are using VCL thread objects and Synchronize method,
// when using VCL objects from the threads).
// 2 :) VirtualAlloc may fail! If this happens, the return value is undefined
// Regardless of how unlikely this is to happen, it can happen. The thing
// is that, if it does happen, the procedure that called MakeObjectInstance
// will raise an exception, when it wants to access the block, anyway.
// 3 :) I am not sure how portable is the way to get the block, based on the
// instance address (Address And Not 4095, which will essentially
// align the Address to a 4 kb value). This works under Win9x/Me,
// NT/2000 and XP. That's enough for now:-)

Function MakeObjectInstance(Method :TWndMethod) :Pointer;
Const
BlockCode :Array[1..2] Of Byte = (
$59, { POP ECX }
$E9); { JMP StdWndProc }
PageSize = 4096;
Var
Block :PInstanceBlock;
Instance :PObjectInstance;
Begin
{$IFDEF VCL_Synchronize}
// Check if the synchronization object has been initialized
If Not ObjInstInit Then
Begin
// If not, initialize it, and tell that it's initialized
InitializeCriticalSection(ObjInstLock);
ObjInstInit := True;
End;
// The code after this line is synchronized between threads.
EnterCriticalSection(ObjInstLock);
// Since there might be a memory allocation failure, the try block
// makes sure that the critical section is released.
Try
{$ENDIF}
// Is there some object instance we can already use?
If InstFreeList = Nil Then
Begin
// No, so allocate a new block of object instances
Block := VirtualAlloc(Nil,
PageSize,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
// No instances from the block are used at the moment
Block^.InstCount := 0;
// Fill in the instruction bytes
Move(BlockCode,
Block^.Code,
SizeOf(BlockCode));
// Tell it where the procedure is
Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2],
@StdWndProc));
// Now, loop, and initialize all object instances
Instance := @Block^.Instances;
Repeat
// The below 3 lines are initializing the instructions
Instance^.Code := $E8; { CALL NEAR PTR Offset }
Instance^.Offset := CalcJmpOffset(Instance,
@Block^.Code);
// This is linked list manipulation
Instance^.Prev := Nil;
Instance^.Next := InstFreeList;
If InstFreeList <> Nil Then
InstFreeList^.Prev := Instance;
InstFreeList := Instance;
Inc(Longint(Instance),
SizeOf(TObjectInstance));
Until Longint(Instance) - Longint(Block) >= SizeOf
(TInstanceBlock);
// Linked list of Instance blocks
Block^.Prev := Nil;
Block^.Next := InstBlockList;
If InstBlockList <> Nil Then
InstBlockList.Prev := Block;
InstBlockList := Block;
End;
// There's certainly some free block - let's get it, and use it!
Instance := InstFreeList;
InstFreeList := Instance^.Next;
Result := Instance;
Instance^.Method := Method;
// VirtualAlloc allocates memory on 4 KB boundary, so we are
// certain that this line will get the right block.
Block := PInstanceBlock(DWORD(Result) And Not 4095);
// Now, tell the program that we used another instance from the
// Instance block.
Inc(Block.InstCount);
{$IFDEF VCL_Synchronize}
Finally
// No need to syncronize any more.
LeaveCriticalSection(ObjInstLock);
End;
{$ENDIF}
End;

Procedure FreeObjectInstance(ObjectInstance :Pointer);
Var Block :PInstanceBlock;
Inst, Last :PObjectInstance;
Begin
If ObjectInstance <> Nil Then
Begin
{$IFDEF VCL_Synchronize}
// Synchronize if needed
EnterCriticalSection(ObjInstLock);
Try
{$ENDIF}
// Linked list manipulation
PObjectInstance(ObjectInstance)^.Prev := Nil;
PObjectInstance(ObjectInstance)^.Next := InstFreeList;
InstFreeList^.Prev := ObjectInstance;
InstFreeList := ObjectInstance;
// Get the parent block
Block := PInstanceBlock(DWORD(ObjectInstance) And Not 4095);
// Decrement the number of used instances
Dec(Block^.InstCount);
// Is any instance used?
If Block^.InstCount = 0 Then
Begin
// If not, we can free this block
// The next while loop will remove all instances
// from the Free List, and only then can the block be freed
// Since the InstCount is 0, ALL of the instances ARE
// in the free list, so we don't need to worry about
// the validity of their fields.
Inst := @Block^.Instances[0];
Last := @Block^.Instances[InstanceCount];
While Inst <> Last Do
Begin
If Inst = InstFreeList Then
Begin
InstFreeList := InstFreeList^.Next;
If InstFreeList <> Nil Then
InstFreeList^.Prev := Nil;
End
Else
Begin
Inst^.Prev^.Next := Inst^.Next;
If Inst^.Next <> Nil Then
Inst^.Next^.Prev := Inst^.Prev;
End;
Inc(Inst); // Essentially, pointers are increased by
// the size of the structure they point ot:-)
End;
If Inst = InstFreeList Then
Begin
InstFreeList := InstFreeList^.Next;
If InstFreeList <> Nil Then
InstFreeList^.Prev := Nil;
End
Else
Begin
Inst^.Prev^.Next := Inst^.Next;
If Inst^.Next <> Nil Then
Inst^.Next^.Prev := Inst^.Prev;
End;
// It's now time to remove the block from the list of
// Instance blocks
If Block = InstBlockList Then
Begin
InstBlockList := InstBlockList^.Next;
If InstBlockList <> Nil Then
InstBlockList^.Prev := Nil;
End
Else
Begin
Block^.Prev^.Next := Block^.Next;
If Block^.Next <> Nil Then
Block^.Next^.Prev := Block^.Prev;
End;
// The block is no longer references in the instance
// list nor Instance block list, and therefore can
// safely be removed
VirtualFree(Block, 0, MEM_RELEASE);
End;
{$IFDEF VCL_Synchronize}
Finally
// Release the lock.
LeaveCriticalSection(ObjInstLock);
// If no more blocks are left, we can delete the critical section
If InstBlockList = Nil Then
Begin
DeleteCriticalSection(ObjInstLock);
ObjInstInit := False;
End;
// NOTE: The section and ObjInstInit are not synchronized.
// However, we don't need to worry about them, since:
// The first form to be created, is certain to be the application
// form (and it's the first time the MOI will be called)
// and at the time, no threads are going to be created.
// Thus, the critical section will be initialized, in
// thread safe environment, and can be safely used after that.
// The last instance to be freed, is certainly the
// application instance. At that time, no threads are likely to
// exist, and after that the application terminates, and so
// the critical section can be safely deleted.
End;
{$ENDIF}
End;
End;
 
还有没有别的好办法呢?我不想修改VCL呀!
进来的朋友帮我提提前,有分哦!
 
下载Delphi6的补丁,或升级到Delphi7试试。
 
哈哈,目前只有这个办法了,D6,D7都没有解决这个问题啊。
 
多人接受答案了。
 
后退
顶部