第12章 内存管理和指针
- 格式:doc
- 大小:159.50 KB
- 文档页数:23
C语言内存使用详解C语言是一种低级语言,开发者可以直接控制内存使用。
了解C语言内存使用的机制和技巧对于编写高效、安全和可靠的程序至关重要。
本文将详细介绍C语言内存使用的知识和技术,并提供一些实用的建议。
在C语言中,内存是以字节为单位进行管理的,通常将内存分为栈和堆两种。
栈是一种自动分配和自动释放内存的数据结构。
它的特点是后进先出(LIFO),即最后分配的内存最先释放。
栈主要用于存储局部变量、函数参数和函数调用的上下文信息。
在函数调用结束后,分配给局部变量的内存会自动释放。
堆是一种动态分配内存的数据结构,程序员可以手动分配和释放内存。
堆的管理需要调用系统提供的函数,如malloc(和free(。
堆主要用于存储动态分配的数据,如数组、结构体和指针。
程序员需要手动管理堆内存,确保及时释放不再使用的内存,否则会造成内存泄漏。
为了更好地使用内存,提高程序的性能和可靠性,下面是一些C语言内存使用的技巧和注意事项:1.使用局部变量:局部变量是保存在栈上的,它们的生命周期与函数的调用关系密切相关。
局部变量不仅可以节约内存,还可以提高程序的执行效率。
2.合理分配静态变量和全局变量:静态变量和全局变量在程序执行过程中一直存在,它们的生命周期不受函数调用的影响。
过多的静态变量和全局变量会占用大量的内存,影响程序的性能。
3. 动态分配内存时要检查返回值:在调用malloc(等动态分配内存的函数时,要检查返回值是否为NULL。
如果返回值为NULL,表示没有足够的内存可用。
处理内存分配失败的情况至关重要,可以提前终止程序或采取其他恰当的措施。
4. 及时释放不再使用的内存:动态分配的内存在不再使用时要及时释放,以避免内存泄漏。
使用free(函数将内存返回给系统,以供其他程序使用。
5.防止指针错误:指针是C语言中非常重要的概念,但也容易出现指针错误,如空指针引用、越界访问等。
使用指针时要特别小心,确保指针正确地指向有效的内存区域。
计算机操作系统中的内存管理和虚拟化技术计算机操作系统是现代计算机体系结构中不可分割的组成部分。
内存管理和虚拟化技术是计算机操作系统的重要功能之一,它们在保证计算机系统性能和安全性方面发挥着重要作用。
一、内存管理技术内存管理技术是操作系统中实现内存资源的高效利用和保护的重要手段。
计算机系统中的内存被划分为多个逻辑单元,各个逻辑单元之间进行切换和管理,以实现多个进程或任务的并发执行。
1. 内存的划分内存划分是内存管理的第一步。
一般情况下,计算机系统将内存划分为操作系统区域和用户区域。
操作系统区域用于存放操作系统内核和相关数据结构,而用户区域用于存放用户程序和数据。
2. 内存映射内存映射是将逻辑地址转换为物理地址的过程。
操作系统通过地址映射表或页表,将逻辑地址映射到实际的物理地址,以实现程序的正确执行和内存的动态管理。
3. 内存分配与回收内存分配与回收是内存管理的核心功能。
操作系统通过内存分配算法,为进程分配内存空间。
而当进程终止或释放内存时,操作系统需要回收这些空间以供其他进程使用。
4. 内存保护内存保护是防止进程之间互相干扰的重要手段。
通过设定访问权限和限制资源的使用,操作系统可以确保每个进程仅能访问自己被分配到的内存空间,从而保护进程的安全性和稳定性。
二、虚拟化技术虚拟化技术是一种将物理资源抽象为逻辑资源,并为不同的用户或应用程序提供独立的逻辑环境的技术。
在计算机操作系统中,虚拟化技术主要包括虚拟内存和虚拟机技术。
1. 虚拟内存虚拟内存是一种将主存和辅助存储器组合使用的技术。
它通过将物理内存的一部分作为虚拟内存空间,将进程的一部分内容从内存转移到硬盘上,以提高内存的利用率和系统的吞吐量。
2. 虚拟机虚拟机技术是将一个物理计算机虚拟为多个逻辑计算机的技术。
通过虚拟化软件的支持,可以在一台物理机上同时运行多个操作系统和应用程序,实现资源的共享和隔离,提高计算机系统的利用率和灵活性。
虚拟化技术在云计算和服务器虚拟化中得到了广泛应用,它极大地提升了计算机系统的效率和灵活性,降低了资源的成本和能源消耗。
C语言高级编程技巧与方法第一章:优化代码结构与功能模块化C语言作为一种高效的编程语言,其高级编程技巧与方法对于提高代码的可读性和维护性非常重要。
优化代码结构与实现功能模块化是实现这一目标的重要方式之一。
1.1 函数的使用与定义合理使用函数可以提高代码的可读性和重用性。
函数的定义应该尽量简单明了,只关注核心功能,代码应尽量避免重复操作。
合理选择函数的返回值类型和参数个数,使得函数调用方便快捷。
1.2 使用预处理指令预处理指令可以在编译之前对代码进行预处理,比如宏定义和条件编译。
合理使用宏定义可以减少代码的冗余和重复,提高代码的可维护性。
条件编译可以根据不同的编译选项选择不同的代码路径,提供更高的灵活性。
1.3 模块化编程将代码按照功能模块化划分,模块之间通过接口进行通信。
这样一方面可以提高代码的可读性和可维护性,另一方面也方便代码的重用。
每个模块应该有清晰的责任和功能,模块之间的接口应该设计合理和规范。
第二章:内存管理与指针技巧在C语言中,内存管理和指针技巧是非常重要的方面。
合理有效地使用内存,以及灵活掌握指针的使用都是高级编程技巧与方法的关键。
2.1 动态内存管理在某些情况下,需要动态申请和释放内存。
使用malloc和free 函数可以实现动态内存管理。
但是在申请内存之后,要及时进行释放,以防止内存泄漏问题的出现。
2.2 指针的使用与优化指针是C语言中的重要概念,熟练掌握指针的使用可大大提高代码的效率。
但是指针也容易引发一些问题,比如空指针和野指针。
因此,在使用指针时要格外小心,确保指针的合法性和安全性。
2.3 内存对齐与缓存优化合理使用内存对齐和进行缓存优化,可以提高代码的性能。
内存对齐可以减少内存访问的时间,缓存优化可以减少缓存的命中次数,从而提高代码的运行速度。
第三章:并发与多线程编程并发与多线程编程是C语言中的高级编程技巧,特别适用于处理具有并发需求的任务。
3.1 并发编程原理与模型理解并发编程的原理和模型,比如同步与互斥、消息传递与共享内存、生产者与消费者模型等。
c语言指针详细讲解
C 语言中指针是非常强大的概念,它允许程序直接访问内存中的数据。
指针在 C 语言中最初是被用于解决内存分配问题而提出的,随着 C 语言的发展,指针也变得愈发重要。
指针的本质是一个存储变量地址的变量。
在 C 语言中,指针通常用符号&来表示,例如&x 表示的是 x 变量的地址。
指针变量存储的是一个内存地址,当程序读取指针变量时,它会读取该地址中存储的数据。
C 语言中可以使用指针进行高效的内存操作。
例如,当程序需要对一个数组元素进行修改时,可以直接用指针修改该元素的值,而不必修改数组名本身。
另外,指针还可以用于动态分配内存,这是 C 语言中一个重要的特性。
指针的使用方法比较灵活,但也需要小心使用。
如果不小心处理指针,可能会导致未知的错误。
例如,当指针指向的内存空间被释放后,程序试图访问该内存空间时可能会导致未定义的行为。
因此,在C 语言中,指针的使用需要更加谨慎。
C 语言中指针是一个非常重要和强大的概念,掌握指针的使用方法可以让程序员写出更加高效和安全的代码。
计算机操作系统内存管理了解内存分配和回收的原理计算机操作系统内存管理是操作系统中极为重要的一部分,它负责管理计算机主存(内存)的分配和回收。
内存分配和回收的原理对于了解计算机系统的运行机制至关重要。
本文将从内存管理的基本概念开始,介绍内存的分配和回收原理。
一、内存管理基本概念内存管理是操作系统中的一个重要功能,其主要任务是将有限的内存资源分配给各个进程,并及时回收不再使用的内存。
内存管理的核心是虚拟内存技术,它将计算机的内存空间划分为若干个固定大小的页或块,每个进程都认为自己拥有整个内存空间。
二、内存分配原理1. 连续分配在早期的操作系统中,内存分配采用的是连续分配原理。
系统将内存分为固定大小的分区,并为每个进程分配连续的内存空间。
这种分配方法简单高效,但会导致内存碎片问题,进而影响系统性能。
2. 非连续分配为解决内存碎片问题,后来的操作系统引入了非连续分配原理。
非连续分配可以分为分页式和分段式两种方式。
- 分页式:将物理内存划分为固定大小的页框,逻辑地址空间也被划分为相同大小的页。
通过页表实现逻辑地址到物理地址的映射。
- 分段式:将逻辑地址空间划分为若干个段,每个段的大小可以不同。
通过段表实现逻辑地址到物理地址的映射。
三、内存回收原理内存回收是指在进程不再使用某块内存时,及时将其释放,使其成为可供其他进程使用的空闲内存。
内存回收涉及到的主要原理有以下几种:1. 清除位图操作系统通过使用一张位图,来记录内存中的空闲块和已分配块的状态。
当一个进程释放内存时,系统会将相应的位图标记为空闲,以便后续进程可以使用。
2. 空闲链表操作系统通过维护一个空闲链表来管理空闲内存块。
当一个进程释放内存时,系统会将该内存块插入空闲链表,使其成为可供其他进程分配的空闲内存。
3. 垃圾回收垃圾回收是指当进程释放内存后,操作系统自动检测并回收无法访问到的对象所占用的内存。
垃圾回收可以通过引用计数和标记清除等算法实现。
四、内存管理策略为了提高内存利用率和系统性能,操作系统采用了一系列内存管理策略:1. 内存分配策略- 最先适应算法:从空闲链表中选择第一个足够大的内存块分配给进程。
C语言各章节知识点总结C语言是一种常用的编程语言,广泛应用于操作系统、嵌入式系统、网络设备等领域。
下面是C语言各章节的知识点总结。
第一章:C语言概述1.C语言的起源和发展历史。
2.C语言的特点和优势。
3.C语言的应用领域和重要性。
4.C语言的编译过程和基本语法规则。
第二章:基本数据类型和运算符1.C语言的基本数据类型,如整型、浮点型、字符型等。
2.基本数据类型的存储范围和格式化输出。
3.C语言的运算符和运算符优先级。
4.表达式和赋值语句。
第三章:控制语句1. 条件语句,如if语句、switch语句。
2. 循环语句,如for循环、while循环、do-while循环。
3. 循环控制语句,如break语句、continue语句。
第四章:数组和指针1.数组的定义和初始化。
2.一维数组和二维数组的使用。
3.字符数组和字符串的处理。
4.指针的定义和操作。
5.数组和指针的关系。
第五章:函数1.函数的定义和调用。
2.函数的参数传递和返回值。
3.局部变量和全局变量。
4.递归函数和函数指针。
5.函数库的使用。
第六章:结构体和共用体1.结构体的定义和初始化。
2.结构体的访问和操作。
3.结构体数组和结构体指针。
4.共用体的定义和使用。
第七章:文件操作1.文件的打开和关闭。
2.文件的读写操作。
3.文件指针和文件的定位。
4.随机访问文件。
5.文件的错误处理和异常处理。
第八章:预处理和编译1.C语言的预处理指令和宏定义。
2.头文件的引用和包含。
3.条件编译和预处理器的工作过程。
4.编译和链接的过程。
第九章:动态内存管理1.动态内存分配和释放。
2. malloc函数和free函数的使用。
3.内存泄漏和内存溢出的问题。
4.堆和栈的区别和管理。
第十章:指针的高级应用1.指针数组和指向指针的指针。
2.函数指针和回调函数。
3.结构体指针和链表的操作。
4.动态内存分配和指针的应用。
第十一章:位运算和位域1.位运算的基本操作,如与、或、非、移位等。
操作系统课程设计内存管理一、课程目标知识目标:1. 理解内存管理的基本概念,掌握内存分配、回收的原理及方法;2. 掌握虚拟内存的原理,了解分页、分段等内存管理技术;3. 了解操作系统中内存保护、共享、碎片处理等相关问题。
技能目标:1. 能够运用所学知识,分析并设计简单的内存管理算法;2. 能够通过编程实践,实现基本的内存分配与回收功能;3. 能够运用虚拟内存技术,解决实际问题。
情感态度价值观目标:1. 培养学生对操作系统中内存管理知识的学习兴趣,激发学生主动探索精神;2. 培养学生的团队协作意识,学会与他人共同解决问题;3. 增强学生的信息安全意识,了解内存管理在操作系统安全中的重要性。
课程性质分析:本课程为操作系统课程设计的一部分,侧重于内存管理方面的知识。
内存管理是操作系统核心功能之一,对于提高系统性能、保障系统安全具有重要意义。
学生特点分析:学生为计算机科学与技术等相关专业的高年级本科生,具备一定的操作系统基础知识,具备一定的编程能力,但可能对内存管理的深入了解和应用尚有不足。
教学要求:1. 结合实际案例,深入浅出地讲解内存管理的基本原理和方法;2. 采用任务驱动法,引导学生通过实践,掌握内存管理技术;3. 注重培养学生的动手能力和创新能力,提高学生解决实际问题的能力。
二、教学内容1. 内存管理概述:介绍内存管理的基本概念、任务和目标;- 教材章节:第2章 内存管理概述- 内容:内存分配、回收原理,内存保护、共享机制。
2. 内存管理技术:讲解物理内存管理和虚拟内存管理技术;- 教材章节:第3章 内存管理技术- 内容:分页管理、分段管理、段页式管理,内存碎片处理。
3. 内存管理算法:分析常见的内存分配和回收算法;- 教材章节:第4章 内存管理算法- 内容:首次适应算法、最佳适应算法、最坏适应算法等。
4. 操作系统内存管理实例分析:结合具体操作系统,分析其内存管理实现;- 教材章节:第5章 操作系统内存管理实例- 内容:Linux内存管理、Windows内存管理。
计算机操作系统的内存管理技术计算机操作系统的内存管理技术是保证计算机系统正常运行的关键之一。
内存是计算机系统中用于存储和运行程序的重要资源,它的管理对于系统的性能、可靠性和安全性都有着重要的影响。
本文将介绍计算机操作系统中常用的内存管理技术,包括分页系统、分段系统和虚拟内存系统。
一、分页系统分页系统是一种以固定大小的页面为单位来管理内存的技术。
在分页系统中,内存被分成大小相等的页面,而程序也被分成大小相等的页面或页框。
通过页表来映射程序中的虚拟地址和物理地址,实现页面与内存之间的映射。
分页系统可以提高内存的利用率,减少外部片上设备的访问时间,提高程序的运行效率。
二、分段系统分段系统是一种以段为单位来管理内存的技术。
在分段系统中,程序被划分为若干个逻辑段,每个段都有独立的属性和逻辑地址空间。
段表用于映射逻辑地址到物理地址,并提供段的访问控制和保护机制。
分段系统能够提供更加灵活的内存管理方式,不同大小的段可以根据程序的需求进行分配和回收,增加系统的可扩展性和运行效率。
三、虚拟内存系统虚拟内存系统是一种将物理内存与磁盘空间结合起来进行管理的技术。
在虚拟内存系统中,每个进程拥有自己独立的虚拟地址空间,而不需要一次性将全部程序加载到内存中。
当程序的某些部分不再使用时,可以将其换出到磁盘上,从而释放出内存空间供其他程序使用。
当程序要访问被换出的页面时,操作系统会将其从磁盘加载到内存中。
虚拟内存系统能够充分利用磁盘空间,提高内存利用率,同时也提供了更大的地址空间。
四、内存管理策略除了以上介绍的内存管理技术,操作系统还需要通过一些策略来管理内存,以保证系统的性能和可靠性。
常用的内存管理策略包括页面置换算法、内存分配算法和内存回收算法。
页面置换算法用于决定哪些页面被置换出去,通常采用FIFO或LRU等算法来实现。
内存分配算法用于分配进程所需的内存空间,可以采用首次适应、最佳适应或最坏适应等算法。
内存回收算法用于回收不再使用的内存空间,可以采用标记-清除、引用计数等算法来实现。
第12章内存管理和指针 (2)12.1 后台内存管理 (2)12.1.1 值数据类型 (2)12.1.2 引用数据类型 (3)12.1.3 垃圾收集 (5)12.2 释放未托管的资源 (5)12.2.1 析构函数 (6)12.2.2 IDisposable 接口 (7)12.2.3 实现IDisposable 接口和析构函数 (8)12.3 不安全的代码 (9)12.3.1 指针 (9)12.3.2 指针示例:PointerPlayaround (16)12.3.3 使用指针优化性能 (20)12.4 小结 (22)第12章内存管理和指针本章介绍内存管理和内存访问的各个方面。
尽管运行库负责为程序员处理大部分内存管理工作,但程序员仍必须理解内存管理的工作原理,了解如何处理未托管的资源。
如果很好地理解了内存管理和C#提供的指针功能,也就能很好地集成C#代码和原来的代码,并能在非常注重性能的系统中高效地处理内存。
本章的主要内容如下:● 运行库如何在堆栈和堆上分配空间● 垃圾收集的工作原理● 如何使用析构函数和System.IDisposable 接口来确保正确释放未托管的资源● C#中使用指针的语法● 如何使用指针实现基于堆栈的高性能数组12.1 后台内存管理C#编程的一个优点是程序员不需要担心具体的内存管理,尤其是垃圾收集器会处理所有的内存清理工作。
用户可以得到像C++语言那样的效率,而不需要考虑像在C++中那样内存管理工作的复杂性。
虽然不必手工管理内存,但如果要编写高效的代码,就仍需理解后台发生的事情。
本节要介绍给变量分配内存时计算机内存中发生的情况。
注意:本节的许多内容是没有经过事实证明的。
您应把这一节看作是一般规则的简化向导,而不是实现的确切说明。
12.1.1 值数据类型Windows使用一个系统:虚拟寻址系统,该系统把程序可用的内存地址映射到硬件内存中的实际地址上,这些任务完全由Windows 在后台管理,其实际结果是32 位处理器上的每个进程都可以使用4GB 的内存——无论计算机上有多少硬盘空间。
(在64 位处理器上,这个数字会更大)。
这个4GB 内存实际上包含了程序的所有部分,包括可执行代码、代码加载的所有DLL,以及程序运行时使用的所有变量的内容。
这个4GB 内存称为虚拟地址空间,或虚拟内存,为了方便起见,本章将它简称为内存。
4GB 中的每个存储单元都是从0 开始往上排序的。
要访问存储在内存的某个空间中的一个值,就需要提供表示该存储单元的数字。
在任何复杂的高级语言中,例如C#、VB、C++和Java,编译器负责把人们可以理解的变量名称转换为处理器可以理解的内存地址。
在进程的虚拟内存中,有一个区域称为堆栈。
堆栈存储不是对象成员的值数据类型。
另外,在调用一个方法时,也使用堆栈存储传递给方法的所有参数的复本。
为了理解堆栈的工作原理,需要注意在C#中变量的作用域。
如果变量a 在变量b 之前进入作用域,b 就会先出作用域。
下面的代码:{int a;// do something{int b;// do something else}}首先声明a。
在内部的代码块中声明了b。
然后内部的代码块终止,b 就出作用域,最后a 出作用域。
所以b 的生存期会完全包含在a 的生存期中。
在释放变量时,其顺序总是与给它们分配内存的顺序相反,这就是堆栈的工作方式。
我们不知道堆栈在地址空间的什么地方,这些信息在进行C#开发是不需要知道的。
堆栈指针(操作系统维护的一个变量) 表示堆栈中下一个自由空间的地址。
程序第一次运行时,堆栈指针指向为堆栈保留的内存块末尾。
堆栈实际上是向下填充的,即从高内存地址向低内存地址填充。
当数据入栈后,堆栈指针就会随之调整,以始终指向下一个自由空间。
这种情况如图11-1 所示。
在该图中,显示了堆栈指针800000(十六进制的0xC3500),下一个自由空间是地址799999。
堆栈指针存储单元已用未用800000799999799998799997图11-1下面的代码会告诉编译器,需要一些存储单元以存储一个整数和一个双精度浮点数,这些存储单元会分别分配给nRacingCars 和engineSize,声明每个变量的代码表示开始请求访问这个变量,闭合花括号表示这两个变量出作用域的地方。
{int nRacingCars = 10;double engineSize = 3000.0;// do calculations;}假定使用如图11-1 所示的堆栈。
变量nRacingCars 进入作用域,赋值为10,这个值放在存储单元799996~799999 上,这4 个字节就在堆栈指针所指空间的下面。
有4 个字节是因为存储int 要使用4 个字节。
为了容纳该int,应从堆栈指针中减去4,所以它现在指向位置799996,即下一个自由空间(799995)。
下一行代码声明变量engineSize(这是一个double),把它初始化为3000.0。
double 要占用8 个字节,所以值3000.0 占据栈上的存储单元799988~799995 上,堆栈指针减去8,再次指向堆栈上的下一个自由空间。
当engineSize 出作用域时,计算机就知道不再需要这个变量了。
因为变量的生存期总是嵌套的,当engineSize 在作用域中时,无论发生什么情况,都可以保证堆栈指针总是会指向存储engineSize 的空间。
为了从内存中删除这个变量,应给堆栈指针递增8,现在指向engineSize 使用过的空间。
此处就是放置闭合花括号的地方。
当nRacingCars 也出作用域时,堆栈指针就再次递增4,此时如果内存中又放入另一个变量,从799999 开始的存储单元就会被覆盖,这些空间以前是存储nRacingCars 的。
如果编译器遇到像int i、j 这样的代码,则这两个变量进入作用域的顺序就是不确定的:两个变量是同时声明的,也是同时出作用域的。
此时,变量以什么顺序从内存中删除就不重要了。
编译器在内部会确保先放在内存中的那个变量后删除,这样就能保证该规则不会与变量的生存期冲突。
12.1.2 引用数据类型堆栈有非常高的性能,但对于所有的变量来说还是不太灵活。
变量的生存期必须嵌套,在许多情况下,这种要求都过于苛刻。
通常我们希望使用一个方法分配内存,来存储一些数据,并在方法退出后的很长一段时间内数据仍是可以使用的。
只要是用new 运算符来请求存储空间,就存在这种可能性——例如所有的引用类型。
此时就要使用托管堆。
如果以前编写过需要管理低级内存的C++代码,就会很熟悉堆(heap)。
托管堆和C++使用的堆不同,它在垃圾收集器的控制下工作,与传统的堆相比有很显著的性能优势。
托管堆(简称为堆)是进程的可用4GB 中的另一个内存区域。
要了解堆的工作原理和如何为引用数据类型分配内存,看看下面的代码:void DoWork(){Customer arabel;arabel = new Customer();Customer otherCustomer2 = new EnhancedCustomer();}在这段代码中,假定存在两个类Customer 和EnhancedCustomer。
EnhancedCustomer类扩展了Customer 类。
首先,声明一个Customer 引用arabel,在堆栈上给这个引用分配存储空间,但这仅是一个引用,而不是实际的Customer 对象。
arabel 引用占用4 个字节的空间,包含了存储Customer 对象的地址(需要4 个字节把内存地址表示为0 到4GB 之间的一个整数值)。
然后看下一行代码:arabel = new Customer();这行代码完成了以下操作:首先,分配堆上的内存,以存储Customer 实例(一个真正的实例,不只是一个地址)。
然后把变量arabel 的值设置为分配给新Customer 对象的内存地址(它还调用合适的Customer()构造函数初始化类实例中的字段,但我们不必担心这部分)。
Customer 实例没有放在堆栈中,而是放在内存的堆中。
在这个例子中,现在还不知道一个Customer 对象占用多少字节,但为了讨论方便,假定是32 字节。
这32 字节包含了Customer 实例字段,和.NET 用于识别和管理其类实例的一些信息。
为了在堆上找到一个存储新Customer 对象的存储位置,.NET 运行库在堆中搜索,选取第一个未使用的、32 字节的连续块。
为了讨论方便,假定其地址是200000,arabel 引用占用堆栈中的799996~799999 位置。
这表示在实例化arabel 对象前,内存的内容应如图11-2所示。
200000堆栈指针未用堆未用堆栈199999已用堆已用堆栈799996~799999arabel图11-2给Customer 对象分配空间后,内存内容应如图11-3 所示。
注意,与堆栈不同,堆上的内存是向上分配的,所以自由空间在已用空间的上面。
堆栈指针已用堆栈未用堆未用堆栈799996~799999arabel199999已用堆200032200000~200031arabel 实例图11-3下一行代码声明了一个Customer 引用,并实例化一个Customer 对象。
在这个例子中,需要在堆栈上为mrJones 引用分配空间,同时,也需要在堆上为它分配空间:Customer otherCustomer2 = new EnhancedCustomer();该行把堆栈上的 4 字节分配给otherCustomer2 引用,它存储在799992~799995 位置上,而otherCustomer2 对象在堆上从200032 开始向上分配空间。
从这个例子可以看出,建立引用变量的过程要比建立值变量的过程更复杂,且不能避免性能的降低。
实际上,我们对这个过程进行了过分的简化,因为.NET 运行库需要保存堆的状态信息,在堆中添加新数据时,这些信息也需要更新。
尽管有这些性能损失,但仍有一种机制,在给变量分配内存时,不会受到堆栈的限制。
把一个引用变量的值赋予另一个相同类型的变量,就有两个引用内存中同一对象的变量了。
当一个引用变量出作用域时,它会从堆栈中删除,如上一节所述,但引用对象的数据仍保留在堆中,一直到程序停止,或垃圾收集器删除它为止,而只有在该数据不再被任何变量引用时,才会被删除。
这就是引用数据类型的强大之处,在C#代码中广泛使用了这个特性。
这说明,我们可以对数据的生存期进行非常强大的控制,因为只要有对数据的引用,该数据就肯定存在于堆上。
12.1.3 垃圾收集由上面的讨论和图可以看出,托管堆的工作方式非常类似于堆栈,在某种程度上,对象会在内存中一个挨一个地放置,这样就很容易使用指向下一个空闲存储单元的堆指针,来确定下一个对象的位置。