7.x: Out of memory when using EurekaLog
Posted by Aleksandr Alekseev on 13 February 2025 12:32
|
|
Problem:My application runs just fine when compiled without EurekaLog. However, if I compile it with EurekaLog:
Out of memory: Allocating: 1112 (1.09 Kb) Largest available: 1183744 (1.13 Mb) Total allocated (RTL): 1193750159 (1.11 Gb) Total allocated (virtual): 2076430336 (1.93 Gb) Total available (page file): 20763758592 (19.34 Gb) Total (page file): 38592802816 (35.94 Gb)
Reason:EurekaLog has memory checks enabled by default. Please note that "leaks checks" and "memory checks" are two different things. You can enable memory checks while having leaks checks disabled (which is a default case). On the other hand, you can't have leaks checks enabled while memory checks are disabled. If memory checks are enabled: each memory allocation in your application will have a larger size to store extra information (header/footer) required for checks to work:
For example:
Conclusion: the larger your allocated memory blocks are = the less overhead. Worst case is like allocating simple TObjects - which are just 8 bytes (.InstanceSize method).
Diagnostic:A). If the "total allocated (virtual)" value is close to the possible maximum of address space for your application:
then you have a real out of memory error, because you have exhausted all free memory in your application. For example: Allocating: 1112 (1.09 Kb) As you can see, the "total allocated (virtual)" value is 1.93 Gb, which is pretty close to the possible maximum of 2.0 Gb for a normal 32-bit app. Therefore, it is an error message about real out of memory error, because you have used up all of your 2 Gb of available memory.
B). If the "total allocated (virtual)" value is not near the possible maximum limit for your application (outlined in the section A above), but the "total available (page file)" value is very low: you have a real out of memory error. It is not because you have exhausted all free memory in your application, but it is because your system does not have enough memory available. For example: Allocating: 456Largest available: 406913024 (388.06 Mb) Total allocated (RTL): 779572038 (743.46 Mb) Total allocated (virtual): 874307584 (833.80 Mb) Total available (page file): 5758976 (5.49 Mb) Total (page file): 1506299904 (1.40 Gb) As you can see, while the "total allocated (virtual)" value (833.80 Mb) is far less than 2 Gb for a 32-bit app, but the paging memory has run out.
Specifically, the total amount of available memory for the system from the sample error message above is 1 Gb of RAM with 512 Mb of swap/page file, resulting in 1.5 Gb of total available memory. So you may hit this limit, and while the "largest available" value would still show the largest continuous free block in your address space to be sufficiently large (or even huge for 64-bit apps), but the system doesn't have free memory to allow your app to actually allocate more memory.
C). If the "total allocated (RTL)" value is significantly smaller than the "total allocated (virtual)" value: you have out of memory error because of the overhead from EurekaLog. For example: Allocating: 1112 (1.09 Kb) The "total allocated (RTL)" value (355.86 Mb) is significantly smaller than the "total allocated (virtual)" value (1.93 Gb) - which means your code allocated a lot of small memory blocks (objects), so now you have a large overhead (about x5.5 times). In other words, you have out of memory error because of additional overhead from EurekaLog memory/leaks checks. On the other hand: Allocating: 1112 (1.09 Kb)Largest available: 1183744 (1.13 Mb) Total allocated (RTL): 1686788239 (1.57 Gb) Total allocated (virtual): 2072498176 (1.93 Gb) Total available (page file): 20763758592 (19.34 Gb) Total (page file): 38592802816 (35.94 Gb) While the "total allocated (RTL)" value (1.57 Gb) is smaller than the "total allocated (virtual)" value (1.93 Gb), but the difference is not that big - resulting is a reasonable x1.2 times overhead. In other words, you do not have out of memory error because of additional overhead from EurekaLog memory/leaks checks.
Important Note: the "total allocated (virtual)" value shows how much memory your application has allocated from the system. It includes 3rd party code (such as non-Delphi code, DLLs, system). Therefore, if some 3rd party code would allocate a lot of memory in your app, the "total allocated (virtual)" value will be significantly higher than the "total allocated (RTL)" value even if you have EurekaLog disabled. That is why it is important to understand when this happen to avoid possible confusion. D). If the "largest available" value is about few Mb in size and/or less than the "Allocating" value, but the "total allocated (virtual)" value is not close to the maximum possible limit (see sections A above) and the "total available (page file)" value is large enough (see section B above): then you have out of memory error because of memory fragmentation. For example: Allocating: 1183744 (1.13 Mb) As you can see, there is plenty of free memory available in your application, because the "total allocated (virtual)" value (1 Gb) is not reaching the limit (2 Gb for a normal 32-bit app, see section A above) and the "total available (page file)" value is not hitting the system's limit (see section B above). However, the "largest available" value is very small - smaller than the requested memory (1.13 Mb). It is the classic symptom of a memory fragmentation: you try to allocate a (large) block and you can't, even though you appear to have enough free memory.
E). If the "largest available" value is more than few Mb in size and is significantly larger than the "Allocating" value, while the "total allocated (virtual)" value is not close to the maximum possible limit (see section A above) and the "total available (page file)" value is large enough (see section B above): you probably do not have an out of memory error at all. You see, memory allocation in Delphi (Builder) is implemented like so: function GetMem(Size: NativeInt): Pointer; As you can see, any memory allocation error is treated like "out of memory" error. So, what this error is saying is "unable to allocate memory" for whatever reason. It could be due to memory corruption bug somewhere. For example, a memory bug could damage control structures of the memory manager. For example: Allocating: 1112 (1.09 Kb) Notice while the "largest available" value (1.13 Mb) is larger than the "allocating" value (1.09 Kb), but it is not significantly larger. The reason the "largest available" value is larger than the "allocating" value is because your application does not allocate memory directly from the system. Instead, your allocation requests go to your app's memory manager, which is the FastMM by default (on modern IDEs). Memory manager allocate larger chunks from the system and then uses these chunks to serve memory allocation requests from your code. Specifically, FastMM uses chunks of about 1.2 Mb in size. As you can see the available free memory (1.13 Mb) is less than 1.2 Mb required for FastMM to allocate a new chunk. Therefore, this case is a real out of memory error. Another example: Allocating: 2097152 (2 Mb) Notice the "largest available" value (1.13 Mb) is smaller than the "allocating" value (2 Mb). Therefore, this case is a real out of memory error. On the other hand: Allocating: 1112 (1.09 Kb) Notice the "largest available" value (125.6 Tb) is larger than few Mb and it is significantly larger than the "allocating" value (1.09 Kb), while the "total allocated (virtual)" is nowhere near close to the possible max and the "total available (page file)" indicate plently of free swap/page file space. All of this means: there is plenty of free memory in application, but request to allocate memory fails. Therefore, it is not a real out of memory error. It is a memory corruption error.
Solution:A). Insufficient system's memory.If you have insufficient system's memory (not enough RAM and swap/page file) - fix it. Enable swap/page file (if disabled), increase size of the swap/page file, and/or install more RAM. You may follow other advices below (especially if you have a large overhead from EurekaLog), but increasing your system's memory is a faster and easier way.
B). Memory corruption.If you have a memory corruption error - start searching for it. We would recommend to enable memory and leaks checks in EurekaLog. Just be sure leaks checks are enabled (for example, you don't run your app outside debugger while having the "Active only when running under debugger" option enabled). If it did not help - try to use full-featured debugging memory manager. Such as FastMM in full debug mode, SafeMM, etc. If it did not help - try to review your code.
C). Memory fragmentation.Memory fragmentation is when most of your memory is allocated in a large number of non-contiguous blocks, or chunks - leaving a good percentage of your total memory unallocated, but unusable for most typical scenarios. If you have a memory fragmentation - start by reviewing your code. First, ensure your code does not leak. Even a smallest leak of just 1 byte being repeated enough times can occupy your free memory dividing it into smaller blocks, thus causing fragmentation. Therefore, check your application for memory and resource leaks. Fix all leaks that you can find. Second, once you are sure there are no leaks, and you left with pure memory fragmenation: review your code. Memory fragmentation is caused by the behavior of an application, it is about greatly different allocation lifetimes. In other words, some blocks/objects are allocated and freed regularly while other persist for long periods of time. The best thing you can do to address this type of a problem is logically divide objects by lifetimes. Basically, you want to have two (or more) heaps (pools) of temporal and permanent blocks/objects, or blocks/objects of a similar life-time. For example, if your application opens a file/document, you may want to allocate everything related to that file/document in a standalone heap/pool. Additionally, you can reduce memory fragmentation by reducing the amount you allocate / deallocate. For example, you can use dynamic arrays as pool of various things, so instead of allocating a million of small objects - it is better to allocate a single array of small records. See also the "C). Out of memory" section below. Notice that you can override the .NewInstance method for some classes to allocate objects differently: say, on an array or via specific heap. Finally, you may try to replace your memory manager with another one that offer low fragmentation allocations. Generally you don't need to worry about memory fragmentation, unless your application is long-running and does a lot of allocation (and freeing). It starts to be a problem when you have a mix of short-lived and long-lived blocks/objects on a long distance. Therefore, if you have such application (such as service) - you may be able to restart it from time to time, if it is possible - thus avoiding memory fragmentation error in the first place.
D). Out of memory due to overhead.Running out of memory because of EurekaLog's overhead means your code allocates a lot of small memory blocks (objects). If you have out of memory error because of added EurekaLog's overhead:
E). Out of memory.You have out of memory error, but it is not because of EurekaLog's overhead. For example, your application allocates 1.8 Gb of memory and runs just fine, but if you add large enough code to your application (not just EurekaLog, any code of a sufficient size) - your application may not fit inside 2.0 Gb. If this is the case, you have no other choice but to decrease memory footprint of your application.
Finally, you can switch to the 64-bit.
See also: | |
|