堆栈

C#是一种托管语言,意味着内存管理是由.NET运行时(CLR - Common Language Runtime)自动处理的。

栈(Stack)

栈是一种后进先出(LIFO, Last In First Out)数据结构。在C#中,栈主要用于存储值类型变量(如int、char、struct)和方法调用的局部变量。栈的特点是分配和释放速度快,因为只需要移动栈指针即可。

  • 值类型:存储在栈上,它们的生命周期由包含它们的上下文决定。当方法调用结束时,它们会自动释放。如果是作为类的成员变量存在,那么存储在堆上,但它们本身仍然是值类型。
  • 方法调用:每次方法调用会在栈上创建一个栈帧(Stack Frame),用于存储该方法的参数、局部变量和返回地址。当方法返回时,相应的栈帧会被销毁。

堆(Heap)(一般是指托管堆(Managed Heap))

堆是一种动态分配内存的区域,适用于存储引用类型(如类对象)。堆中的内存分配和释放是由垃圾回收器(GC)管理的。

  • 引用类型:存储在堆上,变量指针存储在栈上,包含指向堆中对象的引用(地址),它们的生命周期由垃圾回收器决定。引用类型包括类实例、数组等。
  • 内存管理:堆上的对象不会在超出作用域后立即销毁,而是由垃圾回收器根据对象的可达性(是否还有引用指向它)决定何时释放。
    内存泄漏是说,有实际有引用指向的对象,这部分垃圾回收不会回收处理掉,但是软件的预期是该对象应当回收,没有意识到还有引用指向,这就是内存泄漏,因为软件不会使用该对象,垃圾回收也不会处理,这部分内存完全浪费了

非托管堆(Unmanaged Heap)

非托管理堆是由操作系统管理的内存区域,通常用于存储非托管代码(如C/C++代码)创建的对象。非托管内存的分配和释放需要由程序员手动控制(如通过Marshal.AllocHGlobalMarshal.FreeHGlobal)。

  • 内存泄漏风险:由于非托管内存不受垃圾回收器管理,程序员必须确保手动释放内存,否则会导致内存泄漏。
  • 互操作性:在C#中,可以通过P/Invoke或COM与非托管代码进行互操作,需要特别注意内存管理。
大对象堆

大对象堆是托管堆的一部分,具体参见垃圾回收章节

装箱拆箱

装箱是将一个值类型(例如 intstruct 等)转换为一个引用类型(例如 object)的过程。这个过程涉及到将值类型的数据从栈(stack)上移动到堆(heap)上。

过程:

  1. 创建对象: 在堆上分配内存来存储值类型的数据。
  2. 复制值: 将值类型的实际数据从栈中复制到堆上新创建的对象中。
  3. 返回引用: 返回对堆上对象的引用。

堆栈和堆的变化:

  • 栈: num 作为值类型变量存在于栈上。
  • 堆: obj 是一个引用类型变量,它在堆上创建了一个包含 num 值的新对象。

拆箱是将装箱的引用类型数据转换回值类型的过程。这涉及到将堆上的数据复制回栈中。

过程:

  1. 验证类型: 检查堆上的对象是否为目标值类型。
  2. 复制数据: 将堆上的值复制回栈上的值类型变量中。

堆栈和堆的变化:

  • 栈: num 作为值类型变量存在于栈上。
  • 堆: obj 在堆上仍然存在,直到不再被引用并被垃圾回收器回收。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容