Question

I have seen recently a strange behavior in my program. After creating large amounts of objects (500MB of RAM) then releasing them, the program's memory footprint does not return to its original size. It still shows a footprint of 160MB (Private working set).

Normal behavior?

Borland's memory manager does not behave like this, so if possible please confirm (or infirm) this is a normal behavior for FastMM: If you have a handy program in which you create a rather complex MDI child (containing several controls/objects), can you create in a loop 250 instances of that MDI child in memory (at the same time) then release them all and check the memory footprint. Please make sure that you consume at least 200-300MB or RAM with those MDI childs.

Especially those that still using Delphi 7 can see the difference by temporary disabling FastMM.

Thanks


If anybody is interested, especially if you want some proof this is not a memory leak (I hope it is not a mem leak in my code - this is also one of the points of this post: to check if it is my fault), here are the original discussions:

My program never releases the memory back. Why?
How to convince the memory manager to release unused memory

Was it helpful?

Solution 5

SOLVED

To confirm that this behavior is generated by FastMM (as suggested by Barry Kelly) 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. Special thanks to Barry Kelly, the only person that pointed to the real "problem".

OTHER TIPS

IIRC, the Delphi memory manager does not immediately return free'd memory to the OS.

Memory is allocated in chunks of small, medium and large sizes, called blocks. These blocks are kept for a while after their contents have been disposed to have them readyly available when another allocation is requested afterwards.

This limits the amount of system calls required for succesive allocation of multiple objects, and helps avoiding heap fragmentation.

Infirming: Delphi 2007, default memory manager (should be FastMM variation). Several tests on heavy objects:

  1. Initial memory 2Mb, peak memory 30Mb, final memory 4Mb.
  2. Initial memory 2Mb, peak memory 1Gb, final memory 5.5Mb.

Dear Altar, I'm dazzled at how off the point you are in your guesses and how you don't listen to what people told you many times before.

Let's set some things straight. Memory management 101. Please read thoroughly.

When you allocate memory in Delphi, there are two memory managers involved.

System memory manager

First one is a system memory manager. This one is built into Windows and it gives memory in 4kb sized pages.

But it doesn't always give you memory in RAM (or physical memory). Your data can be kept on the hard drive, and read back every time you need to access it. This is awfully slow.

In other words, imagine you have 512Mb of physical memory. You run two programs, each requesting 1Gb of memory. What does OS do?

It grants both requests. Both apps get 1Gb of memory each. Both think all the memory is "in memory". But in fact, only 512Mb can be kept in RAM. The rest is stored in page file, although your app does not know that. It just works slow.

Working set size

Now, what is a "working set size" you are measuring?

It's the part of the allocated memory that is kept in RAM.

If you have an application which allocates 1Gb of memory, and you only have 512 Mb of RAM, then it's working set size will be 512Mb. Although it "uses" 1Gb of memory!

When you run another application which needs memory, OS will automatically free some RAM by moving rarely used blocks of "memory" to the hard drive.

Your virtual memory allocation will stay the same, but more pages will be on the hard drive and less in RAM. Working set size will decrease.

From this, you should have understood by this point, that it's pointless to try and minimize the working set size. You're achieving nothing. You're not freeing memory in any sense. You're just offloading the data to the hard drive.

But the system will do that automatically when it needs to. And there's no point making room in RAM until it's needed. You're just slowing down your application, that's all.

TLDR: "Working set size" is not "how much memory application uses". It's "how much is ready right now". Don't try to minimize it, you're just making things worse.

Delphi memory manager

OS gives you virtual memory in pages of 4Kb. But often you need it in much smaller chunks. For instance, 4 bytes for your integer, or 32 bytes for some structure. The solution?

Application memory manager, such as FastMM or BorlandMM or others.

It's job is to allocate memory in pages from the operating system, then give you small chunks of those pages when you need it.

In other words, when you ask for 14 bytes of memory, this is what happens:

  1. You ask FastMM for 14 bytes of memory.
  2. FastMM asks OS for 1 page of memory (4096 bytes).
  3. OS grants one page of memory, backing it up with RAM (it's stored in actual RAM).
  4. FastMM saves that page, cuts 14 bytes of it and gives to you.

When you ask for another 14 bytes, FastMM just cuts another 14 bytes from the same page.

What happens when you release memory? The same thing backwards:

  1. You release 14 bytes to FastMM. Nothing happens.
  2. You release another 14 bytes. FastMM sees that the 4096 byte page it allocated is now completely unused.
  3. Therefore it releases the page, returning it to the system.

It's worth noting that FastMM cannot release just 14 bytes to the system. It has to release memory in pages. Until the whole page is free, FastMM cannot do a thing. Nobody can.

So, why is my working set size so big, even though I released everything?

First, your working set size is not what you should be measuring. Virtual memory consumption is. But if you have big working set size, your virtual memory consumption will be high too.

What's the problem? You should be able to figure out by this point.

Let's say you allocate 1kb, then 3kb of memory. How much virtual memory have you allocated? 4kb, 1 page.

Now you release 3Kb. How much virtual memory do you use now? 1Kb? No, it's still 1 page. You cannot allocate less than 1 page from the system. You're still using 4096 bytes of virtual memory.

Imagine if you do that 1000 times. 1kb, 3kb, 1kb, 3kb, 1kb, 3kb and so on. You allocate 1000 * 4kb = 4 mb like that, and then you release all the 3kb parts. How much virtual memory do you use now?

Still 4 mb. Because you allocated 1000 pages at first. Of every page you took 1kb and 3kb chunks. Even if you release 3kb chunks, 1kb chunks will continue to keep every single page you allocated in memory. And every page takes 4kb of virtual memory.

Memory manager cannot magically "move" all of your 1kb chunks together. This is impossible, because their virtual addresses can be referenced from somewhere in code. It's not a trait of FastMM.

But why with BorlandMM everything works better?

Coincidence. Maybe it just so happens that BorlandMM gives you memory in a slightly different way than FastMM does. Next thing you know, you change something in your app and BorlandMM acts just like FastMM did. It's impossible for a memory manager to completely prevent this effect, called memory fragmentation.

So what do I do?

Short answer is, not much until this bothers you.

You see, with modern operating systems, you're not really eating anyone's RAM. Per above, OS will automatically swap your pages out when it needs RAM for other applications. This should not be a concern.

And the "excessive" memory isn't lost. Although pages are allocated, 3kb of each is marked as "free". Next time your app needs memory, memory manager will use that space.

But if you really want to help it, you should reorganize your allocations so that the ones you're planning on keeping are done first, and the ones you will soon release are all allocated after that.

Like this: 1kb, 1kb, 1kb, ..., 3kb, 3kb, 3kb...

If you now release all the 3kb chunks, your virtual memory consumption will drop significantly.

This is not always possible. If it's impossible, then just do nothing. It's more or less alright like it is.

And P.S.

You shouldn't be allocating 500 forms in the first place. This is clearly not a way to go. Fix this, and you won't even have a need to think about memory allocation and releasing.

I hope this clears things up, because four posts on the same topic, frankly, is a bit too much.

What are the heapmanager stats (GetHeapStatus) on the point that 160MB is still allocated?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top