Automatic Memory Management

  最近组里的产品被客户“找茬”,说内存占用太高。不过这是所有托管型的应用程序的一个共同点,.NET和Java程序都是这样,不到内存不够时不会主动释放内存。大家用iOS系统时,如果注意看内存使用情况的话,会发现通常情况下内存是99%左右的占用率,即使没开什么程序。

  为了更好地了解并“帮忙解决”这个问题,现在学习一下.NET的托管和非托管资源的管理机制,以下大部分内容摘抄自MSDN(百度显示英文的功能很扯淡,一个单词会拆成两行,大家凑合着看吧)。

  =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

  Allocating Memory

  When you initialize a new process, the runtime reserves a contiguous region of address space for the process. This reserved address space is called the managed heap. The managed heap maintains a pointer to the address where the next object in the heap will be allocated. Initially, this pointer is set to the managed heap’s base address. All reference types are allocated on the managed heap.When an application creates the first reference type, memory is allocated for the type at the base address of the managed heap. 

  Allocating memory from the managed heap is faster than unmanaged memory allocation. Because the runtime allocates memory for an object by adding a value to a pointer, it is almost as fast as allocating memory from the stack.

  Releasing Memory

  The garbage collector’s optimizing engine determines the best time (说明用户是无法控制的)to perform a collection based on the allocations being made. When the garbage collector performs a collection, it releases the memory for objects that are no longer being used by the application. It determines which objects are no longer being used by examining the application’s roots. An application’s roots include global and static object pointers, local variables and reference object parameters on a thread’s stack, and CPU registers. The garbage collector has access to the list of active roots that the just-in-time (JIT) compiler and the runtime maintain. 

  Objects that are not in the graph are unreachable from the application’s roots. The garbage collector considers unreachable objects garbage and will release the memory allocated for them. Once the memory for the reachable objects has been compacted, the garbage collector makes the necessary pointer corrections so that the application’s roots point to the objects in their new locations. It also positions the managed heap’s pointer after the last reachable object. Note that memory is compacted only if a collection discovers a significant number of unreachable objects. If all the objects in the managed heap survive a collection, then there is no need for memory compaction.

  To improve performance, the runtime allocates memory for large objects in a separate heap. The garbage collector automatically releases the memory for large objects. However, to avoid moving large objects in memory,this memory is not compacted.

  Generations and Performance

  To optimize the performance of the garbage collector, the managed heap is divided into three generations: 0, 1, and 2. The runtime’s garbage collection algorithm is based on several generalizations that the computer software industry has discovered to be true by experimenting with garbage collection schemes. First, it is faster to compact the memory for a portion of the managed heap than for the entire managed heap. Secondly, newer objects will have shorter lifetimes and older objects will have longer lifetimes. Lastly, newer objects tend to be related to each other and accessed by the application around the same time.

  The runtime’s 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 1and 2.

  In reality, the garbage collector performs a collection when generation 0 is fullThis is the most efficient approach, because new objects tend to have short lifetimes, and it is expected that many of the objects in generation 0 will no longer be in use by the application when a collection is performed. In addition, a collection of generation 0 alone often reclaims enough memory to allow the application to continue creating new objects.

  After the garbage collector performs a collection of generation 0, it compacts the memory for the reachable objects as explained in Releasing Memory earlier in this topic. The garbage collector then promotes these objects and considers this portion of the managed heap generation 1. As a result, the garbage collector does not have to reexamine the objects in generations 1 and 2 each time it performs a collection of generation 0.

  After the garbage collector performs its first collection of generation 0 and promotes the reachable objects to generation 1, it considers the remainder of the managed heap generation 0. If this does not reclaim enough memory, the garbage collector can perform a collection of generations 2, 1, and 0. After each collection, the garbage collector compacts the reachable objects in generation 0 and promotes them to generation 1. Objects in generation 1 that survive collections are promoted to generation 2. Because the garbage collector supports only three generations, objects in generation 2 that survive a collection remain in generation 2 until they are determined to be unreachable in a future collection.

  Releasing Memory for Unmanaged Resources

  However, unmanaged resources require explicit cleanup. The most common type of unmanaged resource is an object that wraps an operating system resource, such as a file handle, window handle, or network connection. Although the garbage collector is able to track the lifetime of a managed object that encapsulates an unmanaged resource, it does not have specific knowledge about how to clean up the resource. When you create an object that encapsulates an unmanaged resource, it is recommended that you provide the necessary code to clean up the unmanaged resource in a publicDispose method. By providing a Dispose method, you enable users of your object to explicitly free its memory when they are finished with the object. When you use an object that encapsulates an unmanaged resource, you should be aware of Dispose and call it as necessary. For more information about cleaning up unmanaged resources and an example of a design pattern for implementing Dispose, see Garbage Collection.

  =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

  You should prevent users of your application from calling an object’s Finalize method directly by limiting its scope to protected. In addition, you are strongly discouraged from calling a Finalize method for a class other than your base class directly from your application’s code. To properly dispose of unmanaged resources, it is recommended that you implement a public Dispose or Close method that executes the necessary cleanup code for the object. The IDisposable interface provides the Dispose method for resource classes that implement the interface. Because it is public, users of your application can call the Disposemethod directly to free memory used by unmanaged resources. When you properly implement a Dispose method, the Finalize method becomes a safeguard to clean up resources in the event that the Dispose method is not called. For more information on correct implementation, see Implementing a Dispose Method.

  =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

  The methods in this class influence when an object is garbage collected and when resources allocated by an object are released. Properties in this class provide information about the total amount of memory available in the system and the age category, or generation, of memory allocated to an object.

  The garbage collector tracks and reclaims objects allocated in managed memory. Periodically, the garbage collector performs garbage collection to reclaim memory allocated to objects for which there are no valid references. Garbage collection happens automatically when a request for memory cannot be satisfied using available free memory. Alternatively, an application can force garbage collection using the Collect method.

  During a collection, the garbage collector will not free an object if it finds one or more references to the object in managed code. However, the garbage collector does not recognize references to an object from unmanaged code, and might free objects that are being used exclusively in unmanaged code unless explicitly prevented from doing so. The KeepAlive method provides a mechanism that prevents the garbage collector from collecting objects that are still in use in unmanaged code.

  In most cases, finalizers are implemented by overriding the Object.Finalize method; however, types written in C# or C++ implement destructors, which compilers turn into an override of Object.Finalize. In most cases, if an object has a finalizer, the garbage collector calls it prior to freeing the object. However, the garbage collector is not required to call finalizers in all situations. Also, the garbage collector is not required to use a specific thread to finalize objects, or guarantee the order in which finalizers are called for objects that reference each other but are otherwise available for garbage collection.

  In scenarios where resources must be released at a specific time, classes can implement the IDisposable interface, which contains the IDisposable.Disposemethod that performs resource management and cleanup tasks. Classes that implement Dispose must specify, as part of their class contract, if and when class consumers call the method to clean up the object. The garbage collector does not, by default, call the Dispose method; however, implementations of theDispose method can call methods in the GC class to customize the finalization behavior of the garbage collector.

  It is recommended, but not required, that garbage collectors support object aging using generations. A generation is a unit of measure of the relative age of objects in memory. The generation number, or age, of an object tells which generation an object belongs to. Objects created more recently are part of newer generations, and have lower generation numbers than objects created earlier in the application life cycle. Objects in the most recent generation are in generation zero.

✏️ 有任何想法?欢迎发邮件告诉老夫:daozhihun@outlook.com