How could I get the total amount of memory, that allocated by FastMM?
I've tried that:
function GetTotalAllocatedMemory: Cardinal;
var
MMState: TMemoryManagerState;
begin
GetMemoryManagerState(MMState);
Result := MMState.TotalAllocatedMediumBlockSize + MMState.TotalAllocatedLargeBlockSize;
end;
Is it correct?
Anyways it returns something strange. It 5 times less than a value which I can see in Windows task manager. I believe that the amount of memory allocated by a Delphi application equals FastMM allocated memory plus some system overhead. Am I wrong?
Use this:
//------------------------------------------------------------------------------
// CsiGetApplicationMemory
//
// Returns the amount of memory used by the application (does not include
// reserved memory)
//------------------------------------------------------------------------------
function CsiGetApplicationMemory: Int64;
var
lMemoryState: TMemoryManagerState;
lIndex: Integer;
begin
Result := 0;
// get the state
GetMemoryManagerState(lMemoryState);
with lMemoryState do begin
// small blocks
for lIndex := Low(SmallBlockTypeStates) to High(SmallBlockTypeStates) do
Inc(Result,
SmallBlockTypeStates[lIndex].AllocatedBlockCount *
SmallBlockTypeStates[lIndex].UseableBlockSize);
// medium blocks
Inc(Result, TotalAllocatedMediumBlockSize);
// large blocks
Inc(Result, TotalAllocatedLargeBlockSize);
end;
end;
You are comparing apples and oranges.
FastMM memory is netto usage of memory allocated through FastMM.
This does not include at least these:
FastMM overhead
Windows overhead of blocks allocated by FastMM on your behalf
Windows overhead of things not allocated by FastMM (like the space occupied by DLL's in your process space)
for GUI apps: overhead of GDI, GDI+, DirectX, OpenGL and other storage for visual objects allocated on your behalf.
--jeroen
For the process memory use this:
//------------------------------------------------------------------------------
// CsiGetProcessMemory
//
// Return the amount of memory used by the process
//------------------------------------------------------------------------------
function CsiGetProcessMemory: Int64;
var
lMemoryCounters: TProcessMemoryCounters;
lSize: Integer;
begin
lSize := SizeOf(lMemoryCounters);
FillChar(lMemoryCounters, lSize, 0);
if GetProcessMemoryInfo(CsiGetProcessHandle, #lMemoryCounters, lSize) then
Result := lMemoryCounters.PageFileUsage
else
Result := 0;
end;
I also have faced this situation:
Anyways it returns something strange. It 5 times less than a value
which I can see in Windows task manager. I believe that the amount of
memory allocated by a Delphi application equals FastMM allocated
memory plus some system overhead. Am I wrong?
and wasted several hours trying to find out where all the memory is. My app occupied 170 Mb according to Task manager but FastMM's stats was showing total size of allocated blocks ~13 Mb:
12565K Allocated
160840K Overhead
7% Efficiency
(excerpt from FastMM LogMemoryManagerStateToFile procedure output). Finally I realized that this enormous overhead is caused by FullDebug mode. It keeps stacktraces for every allocation so if you've many tiny memory blocks allocated (my app had UnicodeString x 99137, Unknown x 17014 and ~10000 of Xml objects) the overhead becomes frightening. Removing FullDebug mode returned memory consumption to its normal values.
Hope this help somebody.
Related
In Delphi 10.4, I have a record that I use like this in a TList (System.Generics.Collections):
uses
System.Generics.Collections;
type
TSomething = record
Name: String;
Foo: String;
Bar: String;
Group: String;
Flag1: Boolean;
Flag2: Boolean;
Flag3: Boolean;
Flag4: Boolean;
Flag5: Boolean;
end;
PTSomething = ^TSomething;
//Simplified code for readability...
var
Something: TSomething;
MyList := TList<TSomething>;
lRecP: PTSomething;
MyList := TList<TSomething>.Create;
while ACondition do
begin
Something.Name := 'Something';
//Fill the rest of the record
MyList.Add(Something); //Add is done in a while loop which result around 1000 items in MyList
end;
//Later...
for i := 0 to MyList.Count - 1 do
begin
if ACondition then
begin
lRecP := #MyList.List[i];
lRecP.Name := 'Modified'; //Items can be modified but never deleted
end;
end;
//Later...
MyList.Free;
Is my code prone to memory fragmentation? I have about 1000 records in my list that I will iterate through and maybe modify a string off the record once per record.
Would there be a better way to do what I want to do?
Records lie in intrinsic dynamic array of TSomething. This array will be reallocated when you add new records and expanding is required. Memory manager cares about memory allocation, deallocation, it tries to minimize fragmentation. For list size of 1000 fragmentation should be negligible.
Dynamic array capacity changes rarely to avoid expensive operations of reallocation and to diminish fragmentation (more info in SilverWarior
comment)
You records contain strings. Strings are really pointers, and strings bodies are in another place of memory. Again - memory manager cares about string allocation/deallocation, it does this work well (applications with instant creation, treatment and deallocation of millions of strings work 24/7 many years).
So frequent changing of strings does not affect on the list body (intrinsic array) (until you add new records/ delete existing ones), and unlikely can cause memory fragmentation.
This is not an Answer to your Code. I don´t know if Fragmentation happens. In my experience it is dependent on other things happening in parallel or over time. If your Application has Issues like "E Out Of Memory" after running several days, then its time to look at it.
I Would suggest having a look at FastMM. Using its FastMMUsageTracker
Memory Fragmentation Map over FastMM
For me it was a big help. I had Problems in a Service, but i cant remember where i read about Memory Exhaustion. In FastMM or Mad Except? Sorry i can´t remember. It was a Article explaining why Fragmention happens over time.
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?
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.
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.
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.
SOLVED
As suggested by Barry Kelly the memory will be released automatically by FastaMM.
To confirm that this I crated 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.
consider the next sample application
program TestMemory;
{$APPTYPE CONSOLE}
uses
PsAPI,
Windows,
SysUtils;
function GetUsedMemoryFastMem: cardinal;
var
st: TMemoryManagerState;
sb: TSmallBlockTypeState;
begin
GetMemoryManagerState(st);
result := st.TotalAllocatedMediumBlockSize + st.TotalAllocatedLargeBlockSize;
for sb in st.SmallBlockTypeStates do
begin
result := result + sb.UseableBlockSize * sb.AllocatedBlockCount;
end;
end;
function GetUsedMemoryWindows: longint;
var
ProcessMemoryCounters: TProcessMemoryCounters;
begin
Result:=0;
ProcessMemoryCounters.cb := SizeOf(TProcessMemoryCounters);
if GetProcessMemoryInfo(GetCurrentProcess(), #ProcessMemoryCounters, ProcessMemoryCounters.cb) then
Result:= ProcessMemoryCounters.WorkingSetSize
else
RaiseLastOSError;
end;
procedure Test;
const
Size = 1024*1024;
var
P : Pointer;
begin
GetMem(P,Size);
Writeln('Inside');
Writeln('FastMem '+FormatFloat('#,', GetUsedMemoryFastMem));
Writeln('Windows '+FormatFloat('#,', GetUsedMemoryWindows));
Writeln('');
FreeMem(P);
end;
begin
Writeln('Before');
Writeln('FastMem '+FormatFloat('#,', GetUsedMemoryFastMem));
Writeln('Windows '+FormatFloat('#,', GetUsedMemoryWindows));
Writeln('');
Test;
Writeln('After');
Writeln('FastMem '+FormatFloat('#,', GetUsedMemoryFastMem));
Writeln('Windows '+FormatFloat('#,', GetUsedMemoryWindows));
Writeln('');
Readln;
end.
the results returned by the app are
Before
FastMem 1.844
Windows 3.633.152
Inside
FastMem 1.050.612
Windows 3.637.248
After
FastMem 2.036
Windows 3.633.152
I wanna know why the results of the memory usage are different in the Before and After:
Any memory manager (including FastMM) incurs some overhead, otherwise Delphi could have just used the Windows memory management.
The difference you observe is the overhead:
structures that FastMM uses to keep track of memory usage,
pieces of memory that FastMM did not yet return to the Windows memory management to optimize similar memory allocations in the future.
Because the memory manager is doing clever things in the background to speed up performance.
How does it work getmem/malloc/free?
Heap Allocator - As used by malloc...
1) Internally allocates large chunks(typically 64K up to 1Megabyte) of memory and then sub-divides the chunks up to give you the 100byte and 200byte objects and strings in the program.
When you free memory all that happens is the place where it was allocated from in the internal buffer or chunk is then marked as free. NOTHING ACTUALLY HAPPENS!
2) So you can think of the HEAP as a list of big chunks of memory, and all the objects in your program are just little parts of those chunks.
3) The big internal chunks of memory are only freed when all the objects inside them have been freed, so the usual case is that when you free some object that nothing actually happens except some bits get marked as being available.
That is a fairly naive description of the heap system, but most heaps work in a similar way, but do a lot more optimization than that. But your question is why does the memory not go down and the answer is because nothing actually gets freed. The internal pages of memory are retained for the next call to "new" or "malloc" etc...
PICTURE IT
INSIDE HEAP IS ONE HUGE BLOCK OF 100Kb
You call "malloc(1000)" or "getmem(1000)" to get a 1K block of memory.
Then all that happens is that the 1K block of memory is taken from the 100kb block of memory leaving 99K of memory available in that block. If you keep calling malloc or getmem then it will just keep dividing the larger block up until it needs another larger block.
Each little block of memory allocated with a call to malloc or getmem actually gets about 16 or 24 extra bytes(depending on allocator) extra memory. That memory is bits that the allocator uses to know what is allocated and also where it is allocated.
This is Delphi 2009, so Unicode applies.
I had some code that was loading strings from a buffer into a StringList as follows:
var Buffer: TBytes; RecStart, RecEnd: PChar; S: string;
FileStream.Read(Buffer[0], Size);
repeat
... find next record RecStart and RecEnd that point into the buffer;
SetString(S, RecStart, RecEnd - RecStart);
MyStringList.Add(S);
until end of buffer
But during some modifications, I changed my logic so that I ended up adding the identical records, but as a strings derived separately and not through SetString, i.e.
var SRecord: string;
repeat
SRecord := '';
repeat
SRecord := SRecord + ... processed line from the buffer;
until end of record in the buffer
MyStringList.Add(SRecord);
until end of buffer
What I noticed was the memory use of the StringList went up from 52 MB to about 70 MB. That was an increase of over 30%.
To get back to my lower memory usage, I found I had to use SetString to create the string variable to add to my StringList as follows:
repeat
SRecord := '';
repeat
SRecord := SRecord + ... processed line from the buffer;
until end of record in the buffer
SetString(S, PChar(SRecord), length(SRecord));
MyStringList.Add(S);
until end of buffer
Inspecting and comparing S and SRecord, they are in all cases exactly the same. But adding SRecord to MyStringList uses much more memory than adding S.
Does anyone know what's going on and why the SetString saves memory?
Followup. I didn't think it would, but I checked just to make sure.
Neither:
SetLength(SRecord, length(SRecord));
nor
Trim(SRecord);
releases the excess space. The SetString seems to be required to do so.
If you concatenate the string, the memory manager will allocate more memory because it assumes that you add more and more text to it and allocates additional space for future concatenations. This way the allocation size of the string is much larger than the used size (depending on the used memory manager). If you use SetString, the allocation size of the new string is almost the same as the used size. And when the SRecord string goes out of scope and its ref-count becomes zero, the memory occupied by SRecord is released. So you end up with the smallest needed allocation size for your string.
Try to install memory manager filter (Get/SetMemoryManager), which passes all calls to GetMem/FreeMem to default memory manager, but it also performs stats garhtering. You'll probably see that both variants are equal in memory consumption.
It's just memory fragmentation.
My application builds many objects in memory based on filenames (among other string based information). I was hoping to optimise memory usage by storing the path and filename separately, and then sharing the path between objects in the same path. I wasn't trying to look at using a string pool or anything, basically my objects are sorted so if I have 10 objects with the same path I want objects 2-10 to have their path "pointed" at object 1's path (eg object[2].Path=object[1].Path);
I have a problem though, I don't believe that my objects are in fact sharing a reference to the same string after I think I am telling them to (by the object[2].Path=object[1].Path assignment).
When I do an experiment with a string list and set all the values to point to the first value in the list I can see the "memory conservation" in action, but when I use objects I see absolutely no change at all, admittedly I am only using task manager (private working set) to watch for memory use changes.
Here's a contrived example, I hope this makes sense.
I have an object:
TfileObject=class(Tobject)
FpathPart: string;
FfilePart: string;
end;
Now I create 1,000,000 instances of the object, using a new string for each one:
var x: integer;
MyFilePath: string;
fo: TfileObject;
begin
for x := 1 to 1000000 do
begin
// create a new string for every iteration of the loop
MyFilePath:=ExtractFilePath(Application.ExeName);
fo:=TfileObject.Create;
fo.FpathPart:=MyFilePath;
FobjectList.Add(fo);
end;
end;
Run this up and task manager says I am using 68MB of memory or something. (Note that if I allocated MyFilePath outside of the loop then I do save memory because of 1 instance of the string, but this is a contrived example and not actually how it would happen in the app).
Now I want to "optimise" my memory usage by making all objects share the same instance of the path string, since it's the same value:
var x: integer;
begin
for x:=1 to FobjectList.Count-1 do
begin
TfileObject(FobjectList[x]).FpathPart:=TfileObject(FobjectList[0]).FpathPart;
end;
end;
Task Manager shows absouletly no change.
However if I do something similar with a TstringList:
var x: integer;
begin
for x := 1 to 1000000 do
begin
FstringList.Add(ExtractFilePath(Application.ExeName));
end;
end;
Task Manager says 60MB memory use.
Now optimise with:
var x: integer;
begin
for x := 1 to FstringList.Count - 1 do
FstringList[x]:=FstringList[0];
end;
Task Manager shows the drop in memory usage that I would expect, now 10MB.
So I seem to be able to share strings in a string list, but not in objects. I am obviously missing something conceptually, in code or both!
I hope this makes sense, I can really see the ability to conserve memory using this technique as I have a lot of objects all with lots of string information, that data is sorted in many different ways and I would like to be able to iterate over this data once it is loaded into memory and free some of that memory back up again by sharing strings in this way.
Thanks in advance for any assistance you can offer.
PS: I am using Delphi 2007 but I have just tested on Delphi 2010 and the results are the same, except that Delphi 2010 uses twice as much memory due to unicode strings...
When your Delphi program allocates and deallocates memory it does this not by using Windows API functions directly, but it goes through the memory manager. What you are observing here is the fact that the memory manager does not release all allocated memory back to the OS when it's no longer needed in your program. It will keep some or all of it allocated for later, to speed up later memory requests in the application. So if you use the system tools the memory will be listed as allocated by the program, but it is not in active use, it is marked as available internally and is stored in lists of usable memory blocks which the MM will use for any further memory allocations in your program, before it goes to the OS and requests more memory.
If you want to really check how any changes to your programs affect the memory consumption you should not rely on external tools, but should use the diagnostics the memory manager provides. Download the full FastMM4 version and use it in your program by putting it as the first unit in the DPR file. You can get detailed information by using the GetMemoryManagerState() function, which will tell you how much small, medium and large memory blocks are used and how much memory is allocated for each block size. For a quick check however (which will be completely sufficient here) you can simply call the GetMemoryManagerUsageSummary() function. It will tell you the total allocated memory, and if you call it you will see that your reassignment of FPathPart does indeed free several MB of memory.
You will observe different behaviour when a TStringList is used, and all strings are added sequentially. Memory for these strings will be allocated from larger blocks, and those blocks will contain nothing else, so they can be released again when the string list elements are freed. If OTOH you create your objects, then the strings will be allocated alternating with other data elements, so freeing them will create empty memory regions in the larger blocks, but the blocks won't be released as they contain still valid memory for other things. You have basically increased memory fragmentation, which could be a problem in itself.
As noted by another answer, memory that is not being used is not always immediately released to the system by the Delphi Memory Manager.
Your code guarantees a large quantity of such memory by dynamically growing the object list.
A TObjectList (in common with a TList and a TStringList) uses an incremental memory allocator. A new instance of one of these containers starts with memory allocated for 4 items (the Capacity). When the number of items added exceeds the Capacity additional memory is allocated, initially by doubling the capacity and then once a certain number of items has been reached, by increasing the capacity by 25%.
Each time the Count exceeds the Capacity, additional memory is allocated, the current memory copied to the new memory and the previously used memory released (it is this memory which is not immediately returned to the system).
When you know how many items are to be loaded into one of these types of list you can avoid this memory re-allocation behaviour (and achieve a significant performance improvement) by pre-allocating the Capacity of the list accordingly.
You do not necessarily have to set the precise capacity needed - a best guess (that is more likely to be nearer, or higher than, the actual figure required is still going to be better than the initial, default capacity of 4 if the number of items is significantly > 64)
Because task manager does not tell you the whole truth. Compare with this code:
var
x: integer;
MyFilePath: string;
fo: TfileObject;
begin
MyFilePath:=ExtractFilePath(Application.ExeName);
for x := 1 to 1000000 do
begin
fo:=TfileObject.Create;
fo.FpathPart:=MyFilePath;
FobjectList.Add(fo);
end;
end;
To share a reference, strings need to be assigned directly and be of the same type (Obviously, you can't share a reference between UnicodeString and AnsiString).
The best way I can think of to achieve what you want is as follow:
var StrReference : TStringlist; //Sorted
function GetStrReference(const S : string) : string;
var idx : Integer;
begin
if not StrReference.Find(S,idx) then
idx := StrReference.Add(S);
Result := StrReference[idx];
end;
procedure YourProc;
var x: integer;
MyFilePath: string;
fo: TfileObject;
begin
for x := 1 to 1000000 do
begin
// create a new string for every iteration of the loop
MyFilePath := GetStrReference(ExtractFilePath(Application.ExeName));
fo := TfileObject.Create;
fo.FpathPart := MyFilePath;
FobjectList.Add(fo);
end;
end;
To make sure it has worked correctly, you can call the StringRefCount(unit system) function. I don't know in which version of delphi that was introduced, so here's the current implementation.
function StringRefCount(const S: UnicodeString): Longint;
begin
Result := Longint(S);
if Result <> 0 then
Result := PLongint(Result - 8)^;
end;
Let me know if it worked as you wanted.
EDIT: If you are afraid of the stringlist growing too big, you can safely scan it periodically and delete from the list any string with a StringRefCount of 1.
The list could be wiped clean too... But that will make the function reserve a new copy of any new string passed to the function.