unity / C#值类型、引用类型、栈、堆、垃圾回收GC(理论)

目录

一、值类型和引用类型概念

值类型(value):

引用类型(feference):

引用类型在堆上,值类型在栈上?

二、值类型和引用类型的区别是什么

三、那么堆和栈分别是什么 

四、垃圾回收(Garbage Collector,GC)


声明:本内容来自各种资料和个人总结

扫描二维码关注公众号,回复: 15697523 查看本文章

一、值类型和引用类型概念

值类型(value):

int float bool struct 枚举(enum)等

引用类型(feference):

class  数组是引用,即使元素是值类型(int[]是引用)  委托(delegate)是引用  接口(interface)是引用,但可用值类型实现等。

引用类型在堆上,值类型在栈上?

  1. 引用类型的实例总是在堆上的没有错,但是方法内部声明的变量和方法参数在栈上。 而且实例变量的值总是存储在实例本身存储的地方。例如如一个类中有个int变量,虽然它是一个值类型,但它在堆上。
  2. 引用类型的变量包含了两个存储位置:直接和变量关联的存储位置,以及由变量中存储的值引用的存储位置。
  3. 对于直接与变量关联的存储位置和值类型变量关联的存储位置,它们的存储位置没有区别,也就是引用本身会和值类型一样,如果一直变量短时间存在,就在栈的临时存储池中分配。

二、值类型和引用类型的区别是什么

  1. 赋值后,两个值是否同步变化的区别,值类型赋值完,各自独立。引用类型,赋值完变为一体,一方变化,另一方也会变化。 

三、那么堆和栈分别是什么 

栈是内存中预留的特殊空间,专门用于存储小的、短期的数据值,这些值一旦超出作用就会自动释放,因此成为栈。与数据结构中的栈一样,内存栈也有入栈和出栈。
所有声明过的本地变量(比如int a;)都会放入栈内,当调用函数时处理他们的加载和卸载。也就是说在程序运行前变量已经定义到栈中了。这些函数调用通过所谓的调用栈进行拓展与收缩,当对当前函数完成调用栈处理时,它跳回调用栈之前的调用点,并从之前离开的位置继续执行剩余内容。之前内存分配的位置总是已知的,不需要执行内存清理操作,因为新的内存分配指挥覆盖旧数据,因此栈是相对高效的。
栈的总大小通常很小,大约为兆字节(MB)。当分配超过栈可支持的空间时,可能会导致栈溢出,这会出现在执行大量调用栈时(例如无限循环)或有大量本地变量时,但大多数时候,尽管栈的大小相对较小,但很少会引起栈溢出。

堆表示所有其他的内存空间,并用于大多数内存分配**。由于我们想让大多数内存分配的持有时间比当前函数调用更长,因此不能栈上分配他们,因为栈会在方法执行结束后覆盖执行前后产生的结果。因为数据类型有时往往过大,或者需要保留到函数之外,这时就有了堆。在物理上堆和栈并没有什么区别,它们都只是内存空间,包含存在于RAM中的数据字节。操作系统会请求并保存这些数据字节。不同之处在于使用它们的时机、场合和方式。
在本地代码中,例如用C++编写的语言,这些内存分配通过手动处理,我们有责任确保正确地分配所有内存块,并在不需要时显式地释放内存。不然容易造成内存泄漏,直到内存不够,程序崩溃为止。
在托管语言中,内存释放通过垃圾回收器自动处理,在Unity程序的初始化期间,Mono平台向操作系统申请一串内存,用于生成堆内存空间(通常称为托管堆),供C#代码使用。这个堆空间开始相对较小,不到1MB,但是随着脚本代码需要新的内存块而增长。如果Unity不再需要它,那么该空间可以通过释放回操作系统来缩小。

四、垃圾回收(Garbage Collector,GC)

垃圾回收器(Garbage Collector,GC)有一个重要的工作,该工作确保不使用比所需要的更多的托管内存,而不再需要的内存会自动回收。也就是说,对象的创建以及销毁,GC都会参与其中。例如:如果创建一个GameObject,接着销毁它,那么GC将标记该对象使用的内存空间,以便在后续的时间回收这段内存。注意,内存回收不是实时的,而是只在不要的时候才会回收内存。

Unity使用的Mono版本中的GC是一种追踪式GC,它使用标记与清除策略。该算法分为两个阶段:

每个分配的对象通过一个额外的数据位追踪。该数据位表示对象是否被标记。这些标记设置为false,标识它尚未被标记。当收集过程开始时,它通过设置对象的标识位Alive为true,标记所有依然对程序可访问的对象。可访问对象要么是直接引用(例如栈上的静态或本地变量),要么是通过其他直接或间接可访问对象的字段(成员变量)来间接引用。理论上所有没有被引用的对象都应该被回收。
第二阶段涉及迭代这类引用(GC将在程序的整个生命周期中跟踪这些引用),并基于它的标记状态决定是否应该回收。如果对象未被标记,就会被视为回收的候选者。这个阶段会直接跳过已被标记过的对象,但在下次垃圾回收扫描之前会将它们重新设置为false,以完成新一轮的标记。
当第二个阶段执行结束,所有没被标记的对象会被正式回收以释放空间,然后重新访问创建对象的出事请求,如果GC已经释放了足够的空间,就在新释放的空间内分配内存并返回给调用者。如果释放后的空间不够,就只能再向系统申请更多的托管堆。

事实上,GC在内存中维护所有对象的列表,而应用程序维护了另一个独立的列表,其中仅包含它们中的一部分。只要程序用完对象,就简单的忘记它的存在,将其从列表中移除,而不去考虑对象是否需要被回收。也就是说,垃圾回收的工作是GC自己独立完成的,程序只需要维护自己的对象列表即可。

游戏对于性能的要求较高,为提高效率,可在场景切换时或资源使用不频繁时调用GC。

猜你喜欢

转载自blog.csdn.net/q1295006114/article/details/130915670