前言:记录了总6w字的面经知识点,文章中的知识点若想深入了解,可以点击链接学习。由于文本太多,按类型分开。这一篇是C# 常问问题总结,有帮助的可以收藏。
1.引用类型,值类型
1.1 介绍
值类型:int,bool,float,char,struct,enum。
引用类型:string,object,delegate,interface,class,array。
1.2 区别
- 值类型存储在栈中,引用类型存储在堆中。
- 值类型存储快,引用类型存储慢。
- 值类型表示实际数据,引用类型表示指向在内存堆中的指针和引用。
- 值类型在栈中可以自动释放,引用类型在堆中需要GC来释放
- 值类型继承与 System.ValueType,(System.ValueType继承于System.Object),引用类型继承于System.Object。
- 值类型在栈中存储的是直接的值,引用类型数据本身实在堆中,栈中存放的是一个引用的地址。
1.3 底层
1.引用类型在实例化时,先在栈内开辟空间,用于存储堆中对象的地址,然后在堆内开辟空间,存储引用对象。
2.而值类型直接在栈中开辟空间存储对象。值类型也有引用地址,但都在栈内的同一空间。
3.在参数对象进入方法体内,实则是在栈中开辟了新的临时空间。(也就是参数对象的副本)栈内值类型的修改,由于栈中地址不同,所以值类型不会影响到主体。而引用类型的存储数据是一个堆内的地址,所以对于引用类型的修改是直接修改堆内的对象。
4.值类型对象中的引用类型在堆中(struct中定义的string等)
引用类型对象中的值类型也在堆中(class中的int等)
详细请看:
C# “值类型“和“引用类型“在内存的分配_生产队的驴.的博客-CSDN博客_值类型和引用类型如何分配内存
2.String引用类型的特殊性
2.1 介绍
string的修改,实则是new 一个新的string,在堆内新开辟空间。而此时栈内的副本也会指向堆内新对象。因此string改变。
是新建的对象,和本体没有联系。
2.2 解决
当频繁堆一个字符串进行修改时,利用StringBuilder代替String
2.3 StringBuilder的底层实现?
StringBuilder 是支持扩容的(char类型)数组,在每次空间不足时,会开辟原先数组大小的容量,类似于链表,新建的数组指向上一个已经用完的数组,本身不会产生gc。
具体可以查阅以下知识点:
String/StringBuilder/ToString()底层代码解析( JAVA / C# )_tostring()方法的实现?底层原理_元气小羊.的博客-CSDN博客本章笔记直接记录的string、StringBuilder内存存储原理,并没有大幅度、重点的去介绍堆、栈、常量池的相关底层实现原理。所以,为了帮助大家更好的去理解,可以在阅读本文章前去了解堆、栈、常量池之间的基础关系,对以下的阅读有很大的帮助。ToString底层代码解析(C#/JAVA)——分别浅谈 C# / JAVA 底层实现代码与区别...https://blog.csdn.net/weixin_46484674/article/details/124951029
浅谈C#字符串构建利器StringBuilder_溪源More的博客-CSDN博客前言在日常的开发中StringBuilder大家肯定都有用过,甚至用的很多。毕竟大家都知道一个不成文的规范,当需要高频的大量的构建字符串的时候StringBuilder的性能是要高...https://blog.csdn.net/farway000/article/details/123267500
2.4 扩展:
StringBuffer是线程安全,一般用于多线程(C#端不存在)
StringBuilder是非线程安全,所以性能略好,一般用于单线程
2.5 用StringBuilder拼接字符串就一定比string要好吗?
答:极少拼接(或者短字符串)的情况下 String甚至优于StringBuilder,因为String是公用API,通用性好,用途广泛,读取性能高,占用内存较小,Stringbuilder初始化花费时间更大。
2.6 字符串池
字符串池有什么用,原理是什么?
字符串池是CLR一种针对于反复修改字符串对象的优化措施,作用能够一定程度减少内存消耗。原理是内部开辟容器通过键值对的形式注册字符串对象,键是字符串对象的内容,值是字符串在托管堆上的引用。这样当新创建的时候,会去检查,如果不存在就在这个容器中开辟空间存放字符串。
3. GC
3.1 概念
- unity内部有两个内存管理池:堆内存和栈内存。栈内存(stack)主要用来存储较小的和短暂的数据,堆内存(heap)主要用来存储较大的和存储时间较长的数据。unity中的变量只会在堆栈或者堆内存上进行内存分配,变量要么存储在栈内存上,要么处于堆内存上。
- 只要变量处于激活状态,则其占用的内存会被标记为使用状态,则该部分的内存处于被分配的状态。
- 一旦变量不再激活,则其所占用的内存不再需要,该部分内存可以被回收到内存池中被再次使用,这样的操作就是内存回收。处于栈上的内存回收及其快速,处于堆上的内存并不是及时回收的,此时其对应的内存依然会被