How to convince the memory manager to release unused memory
Asked Answered
I

4

6

In a recent post ( My program never releases the memory back. Why? ) I show that when using FastMM, the application does not release substantial amounts of memory back to the system. Recently I created an artificial test program to make sure the issue it is not a memory and that it only appears with FastMM.

In this program I create and destroy an object (same as the one used in the previous post) 500 times.

The memory requirements are ("Private working set"):

Without FastMM
Before running the loop: 1.2MB
After running the loop: 2.1MB

With FastMM (aggressive debug mode)
Before running the loop: 2.1MB
After running the loop: 25MB

With FastMM (release mode)
Before running the loop: 1.8MB
After running the loop: 3MB

If I run the loop several times, the memory requirement does not increase. Which means that the unreleased memory is re-used so this is not a memory leak (a memory leak would increase the memory footprint with several KB/MB at each run).


My questions are:

How can I disable this behavior in FastMM? Is it even possible? I know, if I release the program without FastMM or with FastMM Release Mode it will "waste" moderate amounts of RAM. But disabling this behavior on demand, will help me (us?) identifying memory leaks. Actually in my first post (see link) many people suggested that I have a leak. The confusion was created obviously just because of this behavior. No, it is obvious there is no leak. It is just the memory manager that refuses to release large amounts of memory.

It will ever release the extra memory? When? What triggers this? Can the programmer trigger it? For example when I know that I have finished a RAM-intensive task and the user may not use the program for a while (minimize it), can I flush the RAM back to the system? What happens when the user open multiple instances of my program? Won't they compete for RAM?

Immoralist answered 17/12, 2010 at 23:14 Comment(0)
I
2

SOLVED

As suggested by Barry Kelly the memory will be released automatically by FastaMM. To confirm this, I created a second program that allocated A LOT of RAM. As soon as Windows ran out of RAM, my program memory utilization returned to its original value.

Problem solved. Thanks, Barry.

Immoralist answered 27/12, 2010 at 16:47 Comment(0)
E
11

You shouldn't think about it as "wasting" RAM, really. Think about it as "caching" unused RAM. The memory manager is holding onto the unused memory instead of releasing it back to the OS for a reason, and in fact you've hit upon that reason in your question.

You said that you keep re-running the same operations in a loop. When you do that, it still has the old memory available and it can assign it immediately, instead of having to ask Windows for a new chunk of heap. This is one of the tricks that puts the "Fast" in "FastMM," and if it didn't do that you'd find your program running a lot more slowly.

You don't need to worry about the FastMM debug mode figure. That's only for debugging, and you're not going to release a program compiled against FullDebugMode. And the difference between "without FastMM" and "with FastMM Release Mode" is about 1 MB, which is negligible on modern hardware. For the low cost of only 1 extra MB, you get a big performance boost. So don't worry about it.

Ensoll answered 17/12, 2010 at 23:35 Comment(2)
Hi Mason. "...is holding onto the unused memory instead of releasing it back to the OS for a reason" - - - God! I am not disputing the benefits of caching!!! Caching is good I know! But I would like some control over it - at least for debugging purposes. Wouldn't be nice to know the EXACT amount of RAM your app needs? :)Immoralist
"For the low cost of only 1 extra MB, you get a big performance boost" - - - That is the amount of "wasted" RAM when I create/destroy one single object at a time (500 times). But if I load 500 objects then free them all, the amount of unreleased RAM (by FastMM) is pretty impressive! Borland's mem manager returns most of the RAM instantly! A user with a 1 GB RAM EEEPC and Windows 7 will not be able to start the second instances of my program even if the first instance is idle as it didn't released back the memory.Immoralist
B
10

Part of what makes FastMM fast is that it will allocate a large block of memory and carve smaller uniformly sized pieces out of it. If any part of the block is in use, none of it can be released back to the OS.

You're welcome to use a different memory manager. One approach would be to route all allocations directly to VirtualAlloc. Allocations will be rounded up to occupy an entire page at a time, so your program may suffer if you have lots of small allocations, but when you call VirtualFree, you can be confident that the memory definitely doesn't belong to your program anymore.

Another option is to route everything to the OS heap. Use HeapAlloc. You can even enable the low-fragmentation heap for your program (on by default as of Windows Vista), which will make the OS employ a strategy similar to the one used by FastMM, but it will allow you to use some debugging and analysis tools from Microsoft to track your program's memory usage over time. Beware, though, that after you call HeapFree, some metrics might still show the memory as belonging to your program.

Besides, the working set refers to the memory that's currently in physical RAM. That you observed the number go up does not mean that your program has allocated any more memory. It can simply mean that your program touched some memory that it had previously allocated, but which had not yet been put into RAM. During your loop, you touched that memory, and the OS has not decided to page it back out to disk yet.

Brocklin answered 18/12, 2010 at 1:11 Comment(0)
P
3

I use the following as a memory manager. I do so because it performs much better under thread contention than FastMM which is actually rather poor. I know that a scalable manager such as Hoard would be better, but this is works fine for my needs.

unit msvcrtMM;

interface

implementation

type
  size_t = Cardinal;

const
  msvcrtDLL = 'msvcrt.dll';

function malloc(Size: size_t): Pointer; cdecl; external msvcrtDLL;
function realloc(P: Pointer; Size: size_t): Pointer; cdecl; external msvcrtDLL;
procedure free(P: Pointer); cdecl; external msvcrtDLL;

function GetMem(Size: Integer): Pointer;
begin
  Result := malloc(size);
end;

function FreeMem(P: Pointer): Integer;
begin
  free(P);
  Result := 0;
end;

function ReallocMem(P: Pointer; Size: Integer): Pointer;
begin
  Result := realloc(P, Size);
end;

function AllocMem(Size: Cardinal): Pointer;
begin
  Result := GetMem(Size);
  if Assigned(Result) then begin
    FillChar(Result^, Size, 0);
  end;
end;

function RegisterUnregisterExpectedMemoryLeak(P: Pointer): Boolean;
begin
  Result := False;
end;

const
  MemoryManager: TMemoryManagerEx = (
    GetMem: GetMem;
    FreeMem: FreeMem;
    ReallocMem: ReallocMem;
    AllocMem: AllocMem;
    RegisterExpectedMemoryLeak: RegisterUnregisterExpectedMemoryLeak;
    UnregisterExpectedMemoryLeak: RegisterUnregisterExpectedMemoryLeak
  );

initialization
  SetMemoryManager(MemoryManager);

end.

This isn't an answer to your question, but it's too long to fit into a comment and you may find it interesting to run your app against this MM. My guess is that it will perform the same way as FastMM.

Pedagogics answered 18/12, 2010 at 11:10 Comment(0)
I
2

SOLVED

As suggested by Barry Kelly the memory will be released automatically by FastaMM. To confirm this, I created a second program that allocated A LOT of RAM. As soon as Windows ran out of RAM, my program memory utilization returned to its original value.

Problem solved. Thanks, Barry.

Immoralist answered 27/12, 2010 at 16:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.