Pay Attention to Memory Managment(Heap memory and stack)
I was justified by a problem with a high number of users, I had a endpoint that create image when call it, When more than 1000 user call it make problem and increase Memory Usage and increase number a large object count (LOH), I can solve this problem with decrease number of large objects splite it to bytes.
Fundamentals of garbage collection
In the common language runtime (CLR), the garbage collector (GC) serves as an automatic memory manager. The garbage collector manages the allocation and release of memory for an application. Therefore, developers working with managed code don’t have to write code to perform memory management tasks. Automatic memory management can eliminate common problems such as forgetting to free an object and causing a memory leak or attempting to access freed memory for an object that’s already been freed.
This article describes the core concepts of garbage collection.
Benefits
The garbage collector provides the following benefits:
- Frees developers from having to manually release memory.
- Allocates objects on the managed heap efficiently.
- Reclaims objects that are no longer being used, clears their memory, and keeps the memory available for future allocations. Managed objects automatically get clean content to start with, so their constructors don’t have to initialize every data field.
- Provides memory safety by making sure that an object can’t use for itself the memory allocated for another object.
Fundamentals of memory
The following list summarizes important CLR memory concepts:
- Each process has its own, separate virtual address space. All processes on the same computer share the same physical memory and the page file, if there’s one.
- By default, on 32-bit computers, each process has a 2-GB user-mode virtual address space.
- As an application developer, you work only with virtual address space and never manipulate physical memory directly. The garbage collector allocates and frees virtual memory for you on the managed heap.
- If you’re writing native code, you use Windows functions to work with the virtual address space. These functions allocate and free virtual memory for you on native heaps.
- Virtual memory can be in three states:
What happens to small objects?
Small .NET objects are allocated onto the Small Object Heaps (SOH). There are three of these: Generation 0, Generation 1, and Generation 2. Objects move up these Generations based on their age.
New objects are placed on Gen 0. When Gen 0 becomes full, the .NET Garbage Collector (GC) runs, disposing of objects which are no longer needed and moving everything else up to Gen 1. If Gen 1 becomes full the GC runs again, but also moves objects in Gen 1 up to Gen 2.
A full GC run happens when Gen 2 becomes full. This clears unneeded Gen 2 objects, moves Gen 1 objects to Gen 2, then moves Gen 0 objects to Gen 1, and finally clears anything which isn’t referenced. After each GC run, the affected heaps are compacted, to keep memory which is still in use together.
This generational approach keeps things running efficiently — the time-consuming compacting process only occurs when absolutely necessary.
Note: if you see a high proportion of memory in Gen 2, it’s an indicator that memory is being held onto for a long time, and that you may have a memory problem. This is where a memory profiling tool, such as Visual Stdio Profiler, can come in handy.
What happens to larger objects?
Objects larger than 85 KB are allocated onto the Large Object Heap (LOH). They aren’t compacted, because of the overhead of copying large chunks of memory. When a full GC takes place, the address ranges of LOH objects not in use are recorded in a free space allocation table instead.
When a new object is allocated, this free space table is checked for an address range large enough to hold the object. If one exists, the object is allocated there, if not, it’s allocated at the next free space.
Because objects are unlikely to be the exact size of an empty address range, small chunks of memory will almost always be left between objects, resulting in fragmentation. If these chunks are less than 85 KB, there’s no possibility of reuse at all. Consequently, as allocation demand increases, new segments are reserved even though fragmented space is still available.
Furthermore, when a large object needs to be allocated, .NET tends to append the object to the end anyway, rather than run an expensive Gen 2 GC. This is good for performance but a significant cause of memory fragmentation.
Garbage Collector Generations
The GC algorithm is based on several considerations:
- It’s faster to compact the memory for a portion of the managed heap than for the entire managed heap.
- Newer objects have shorter lifetimes, and older objects have longer lifetimes.
- Newer objects tend to be related to each other and accessed by the application around the same time.
Garbage collection primarily occurs with the reclamation of short-lived objects. To optimize the performance of the garbage collector, the managed heap is divided into three generations, 0, 1, and 2, so it can handle long-lived and short-lived objects separately. The garbage collector stores new objects in generation 0. Objects created early in the application’s lifetime that survive collections are promoted and stored in generations 1 and 2. Because it’s faster to compact a portion of the managed heap than the entire heap, this scheme allows the garbage collector to release the memory in a specific generation rather than release the memory for the entire managed heap each time it performs a collection.
Generation 0
This generation is the youngest and contains short-lived objects. An example of a short-lived object is a temporary variable. Garbage collection occurs most frequently in this generation.
- Newly allocated objects form a new generation of objects and are implicitly generation 0 collections. However, if they’re large objects, they go on the large object heap (LOH), which is sometimes referred to as generation 3. Generation 3 is a physical generation that’s logically collected as part of generation 2.
- Most objects are reclaimed for garbage collection in generation 0 and don’t survive to the next generation.
- If an application attempts to create a new object when generation 0 is full, the garbage collector performs a collection to free address space for the object. The garbage collector starts by examining the objects in generation 0 rather than all objects in the managed heap. A collection of generation 0 alone often reclaims enough memory to enable the application to continue creating new objects.
Generation 1
This generation contains short-lived objects and serves as a buffer between short-lived objects and long-lived objects.
- After the garbage collector performs a collection of generation 0, it compacts the memory for the reachable objects and promotes them to generation 1. Because objects that survive collections tend to have longer lifetimes, it makes sense to promote them to a higher generation. The garbage collector doesn’t have to reexamine the objects in generations 1 and 2 each time it performs a collection of generation 0.
- If a collection of generation 0 doesn’t reclaim enough memory for the application to create a new object, the garbage collector can perform a collection of generation 1 and then generation 2. Objects in generation 1 that survive collections are promoted to generation 2.
Generation 2
This generation contains long-lived objects. An example of a long-lived object is an object in a server application that contains static data that’s live for the duration of the process.
- Objects in generation 2 that survive a collection remain in generation 2 until they’re determined to be unreachable in a future collection.
- Objects on the large object heap (which is sometimes referred to as generation 3) are also collected in generation 2.
Run profiling tools with or without the debugger
Visual Studio offers a choice of performance measurement and profiling tools. Some tools, like CPU Usage and Memory Usage, can run with or without the debugger, and on release or debug build configurations. Tools that appear in the Diagnostics Tools window run only during a debugging session. Tools that appear in the Performance Profiler run without the debugger and you analyze the results after you choose to stop and collect data (for post-mortem analysis).
When you start debugging in Visual Studio by selecting Debug > Start Debugging, or pressing F5, the Diagnostic Tools window appears by default. To open it manually, select Debug > Windows > Show Diagnostic Tools. The Diagnostic Tools window shows information about events, process memory, CPU usage, and .NET Counters.
Code
Before change and have large object problems.
After change and decrease number of large object count.