.NET 中的内存管理和垃圾回收

作者:微信公众号:【架构师老卢】
7-19 8:34
24

概述:内存管理和垃圾回收是 .NET 开发的基本方面,对于构建高效且可扩展的软件应用程序非常重要。在这篇文章中,我们将讨论堆栈和堆内存,探索内存分配机制,并了解 .NET 中的垃圾回收。照片由 安妮·斯普拉特 on Unsplash了解堆栈和堆内存堆栈内存堆栈是用于执行程序代码和存储方法调用帧、局部变量和函数参数的内存区域。堆栈上的内存以后进先出 (LIFO) 方式进行管理。void ExampleMethod() {     int localVar = 10; // Local variable stored on the stack }在这里,变量存储在堆栈上,当方法退出时,其内存会自动

内存管理和垃圾回收是 .NET 开发的基本方面,对于构建高效且可扩展的软件应用程序非常重要。在这篇文章中,我们将讨论堆栈和堆内存,探索内存分配机制,并了解 .NET 中的垃圾回收。

照片由 安妮·斯普拉特 on Unsplash

了解堆栈和堆内存

堆栈内存

堆栈是用于执行程序代码和存储方法调用帧、局部变量和函数参数的内存区域。堆栈上的内存以后进先出 (LIFO) 方式进行管理。

void ExampleMethod()  
{  
    int localVar = 10; // Local variable stored on the stack  
}

在这里,变量存储在堆栈上,当方法退出时,其内存会自动释放。

堆内存

堆是用于动态分配内存(如对象和数组)的内存区域。堆上的内存由垃圾回收器手动或自动管理。

class MyClass  
{  
    public int MyProperty { get; set; }  
}  
  
void ExampleMethod()  
{  
    MyClass obj = new MyClass(); // Object allocated on the heap  
    obj.MyProperty = 20;  
}

在这里,对象存储在堆上,其内存由垃圾回收器管理。obj

.NET 中的内存分配

值类型与引用类型

在 .NET 中,类型分为值类型或引用类型。值类型存储在堆栈上,而引用类型存储在堆上,对它们的引用存储在堆栈上。

void ExampleMethod()  
{  
    int value = 10; // Value type stored on the stack  
    MyClass reference = new MyClass(); // Reference type stored on the heap  
}

.NET 中的垃圾回收

垃圾回收的基础知识

垃圾回收是自动回收由不再使用的对象占用的内存的过程。.NET 使用分代垃圾回收方法,其中对象根据其年龄分为几代。

class Program  
{  
    static void Main()  
    {  
        MyClass obj = new MyClass();  
        // Code using 'obj'  
        // When 'obj' is no longer referenced, it becomes eligible for garbage collection  
    }  
}

垃圾回收会定期发生,或者在系统确定堆内存不足时进行垃圾回收。在垃圾回收期间,垃圾回收器遍历所有活动的引用,标记可访问的对象,并为无法访问的对象释放内存。

垃圾回收的结构

.NET 利用三代进行垃圾回收:第 0 代、第 1 代和第 2 代。对象从第 0 代开始,并根据其生存情况提升到更高的代数。

  • 第 0 代(Gen 0): 这是最年轻的一代,包含短命的对象。内存分配从这里开始。垃圾回收器在这一代中频繁运行,以快速从不再需要的对象中回收内存。收集第 0 代通常非常快,因为它涉及的对象较少。
  • 第 1 代(第 1 代): 在 Gen 0 垃圾回收中幸存下来的对象将提升为 Gen 1。这一代人充当短期和长期对象之间的缓冲器。收集第 1 代的频率低于第 0 代,但仍旨在有效地回收内存。
  • 第 2 代(第 2 代): 此生成包含长期存在的对象,例如静态数据和长时间使用的对象。就性能而言,收集第 2 代的成本最高,因为它涉及堆的较大部分。因此,对于这一代,垃圾回收器的运行频率较低。

代际集合的工作原理

  • Object Promotion: 当一个对象在其当前一代的垃圾回收中幸存下来时,它将被提升到下一代。例如,Gen 0 中在集合中幸存下来的对象将移动到 Gen 1。
  • Generation Sweeps: 垃圾回收器为不同的世代执行不同类型的扫描。Gen 0 集合是次要集合,而 Gen 2 集合是涉及更全面的内存管理任务的主要集合。

让我们考虑一个示例,看看对象如何在代际之间移动。

using System;

class Program
{
    static void Main()
    {
        // Create object in Generation 0
        MyClass obj1 = new MyClass();

        // Perform some operations
        DoWork();

        // Force a Gen 0 garbage collection
        GC.Collect(0); // Collects Generation 0
        Console.WriteLine("Gen 0 collection completed.");

        // Create more objects
        MyClass obj2 = new MyClass();

        // Perform some more operations
        DoMoreWork();

        // Force a Gen 1 garbage collection
        GC.Collect(1); // Collects Generation 1 and Generation 0
        Console.WriteLine("Gen 1 collection completed.");

        // Create long-lived object
        MyClass obj3 = new MyClass();

        // Simulate application running
        RunApplication();

        // Force a Gen 2 garbage collection
        GC.Collect(2); // Collects Generation 2, Generation 1, and Generation 0
        Console.WriteLine("Gen 2 collection completed.");
    }

    static void DoWork()
    {
        // Allocate some temporary objects
        for (int i = 0; i < 100; i++)
        {
            MyClass tempObj = new MyClass();
        }
    }

    static void DoMoreWork()
    {
        // Allocate more temporary objects
        for (int i = 0; i < 100; i++)
        {
            MyClass tempObj = new MyClass();
        }
    }

    static void RunApplication()
    {
        // Simulate long-running operations
        for (int i = 0; i < 1000; i++)
        {
            MyClass tempObj = new MyClass();
        }
    }
}

class MyClass
{
    public int MyProperty { get; set; }
}

使用分代垃圾回收的最佳实践

  1. 最小化长寿命的对象: 设计应用程序以最大程度地减少长期存在的对象的数量。这样可以降低第 2 代收集的频率和成本。
  2. 正确处理对象: 实现接口和使用语句,确保未管理的资源及时释放。IDisposableusing
  3. 避免大型对象分配: 大型对象直接在大型对象堆 (LOH) 中分配,并在第 2 代收集期间收集。最大程度地减少大型对象分配,以减少对第 2 代集合的影响。
class ResourceHolder : IDisposable  
{  
    private bool disposed = false;  
  
    public void UseResource()  
    {  
        if (disposed)  
        {  
            throw new ObjectDisposedException("ResourceHolder");  
        }  
  
        // Use the resource  
    }  
  
    public void Dispose()  
    {  
        Dispose(true);  
        GC.SuppressFinalize(this);  
    }  
  
    protected virtual void Dispose(bool disposing)  
    {  
        if (!disposed)  
        {  
            if (disposing)  
            {  
                // Release managed resources  
            }  
            // Release unmanaged resources  
            disposed = true;  
        }  
    }  
  
    ~ResourceHolder()  
    {  
        Dispose(false);  
    }  
}

结论:

内存管理和垃圾回收是 .NET 开发的重要组成部分,会影响应用程序性能和可伸缩性。通过应用最佳实践,我们可以优化内存使用、提高应用程序性能并构建高效的 .NET 应用程序。通过这篇文章,我们探讨了 .NET 中的内存管理和垃圾回收。

阅读排行