.Net性能调优——类型揭秘

前言:

从今天起,开始涉及.Net性能调优部分。本篇文章关注.Net类型。阅读本文你将知道:
1..Net如何区分值类型和引用类型
2.值类型和引用类型在内存中如何存储、分配和销毁
3.值类型和引用类型的存储结构
4.为什么lock同步代码块只能是引用类型
5.栈内存为什么效率比堆内存高
6.如何高效的使用值类型替代引用类型,达到提升性能的目的
7.使用值类型的最佳实践
PS:本文纯理论,无源码参考,请谨慎食用

.Net如何区分值类型和引用类型

在语言层面,引用类型具有引用语义,我们考虑的总是对象的标识,而不是其内容。值类型有值语义,对象没有标识,访问对象时是直接访问其内容而不是引用。
.Net通过判断是否继承于ValueType来判断是什么类型。继承于ValueType的是值类型,继承于Object的引用类型。
值类型继承了ValueType,但ValueType实际是重写了Equals,和Object基类的Equals已经没什么关系了,但ValueType的Equals依然会装箱,默认行为是反射获取值类型的所有字段的值依次调用Equals对比

值类型和引用类型在内存中如何存储、分配和销毁

引用类型只从托管堆(由.Net垃圾回收器所管理的区域)上分配,多处理器系统中,如果多个处理器访问堆上的相同的对象,将需要同步。
PS:严格来说,某些引用类型也可以从栈上分配。例如unsafe上下文和stackalloc关键字创建的基元类型的数组(如整型数组),或使用fixed关键字在自定义结构中内嵌一个固定的数组。
单纯的值类型通常在执行线程的栈上分配。方法的"开场白"代码一般会使用一条CPU指令来为所有局部变量在栈上分配存储空间。然后用3个CPU指令销毁整个栈帧,我们称之为"收场白"代码。

值类型和引用类型的存储结构

值类型存多少是多少
引用类型的前4个字节字段称为对象字节头,紧接着的4个字节字段称为方法表指针,然后是存储的数据。
在32位系统中,堆上的对象会对齐到最近的4字节的倍数,64位系统会对齐到8字节的倍数。所以哪怕只存1字节的数据,32位系统上也会占用堆的12字节,64位会占用惊人的24字节。

为什么lock同步代码块只能是引用类型

lock代码块是对Monitor.Enter()和Monitoer.Edit()的封装,这两个函数需要指向同一块内存的指针。而值类型所在的栈内存,函数的栈帧会在方法返回前清空,调用方法获取到的是无效的内存地址的引用。

访问栈内存为什么效率比堆内存高

栈和堆都是虚拟内存上的地址范围,并且线程栈所保留的地址范围与托管堆所保留的地址范围相比,并没有什么先天的优势。
访问栈上的内存地址快于堆上的内存地址原因是:
1.在栈上,时间分配的局部性意味着空间局部性。(分配的时间越接近,存储的位置越接近)。连接在一起的栈能充分利用CPU缓存和操作系统的分页系统,往往具有更好的性能。
2.栈上的内存密度通常比堆上高,这是因为引用类型需要额外的开销。
3.线程栈往往都很小。Windows上默认情况下栈最大为1MB,在当今的系统中,所有的应用程序的线程栈都能用于CPU缓存,这使得栈上对象的访问速度非常的快,相比堆上的对象很少能使用CPU缓存。

如何高效的使用值类型替代引用类型,达到提升性能的目的

重写Equals,==,!=避免比较时的拆装箱。
值类型继承于ValueType,ValueType继承于Object,所以复杂值类型调用Equals是会产生装箱操作,这会让效率降低10倍。
并且,Object的Equals方法在不能直接对比地址值的时候(只有值类型是嵌套式的基础类型时,才会比对地址值),会使用反射逐一对比每一个字段。反射是一种极其“昂贵”的机制,其他任何操作在它面前都会“黯然失色”。
重写GetHashCode
在使用散列表,如HashSet,HashTable,Dictionary时。每次插入都会判断一次HashCode是否包含于Hash表,来防止插入重复数据。合适的重写GetHashCode方法,能让我们在使用散列表时,等到很大的性能提升。

使用值类型的最佳实践

在考虑使用值类型时,应该遵循以下的最佳实践。
1.如果对象很小,并且打算创建很多这样的对象,可以使用值类型
2.如果需要高密度的内存集合,可以使用值类型
3.在值类型中重写Equals、重载Equals、实现IEquatable,以及重载操作符==和!=
4.在值类型中重写GetHashCode
5.考虑将值类型实现为不可变的

总结

本文介绍了值类型和引用类型的实现细节,以及这些细节如何影响应用程序的性能。
值类型展示出了良好的内存密度,可以用于大型集合,但却不具备对象所必需的一些特性,如多态、同步支持和引用语义。
PS:开发者们付出大量努力来正确使用值类型,以得到性能的提升,这是值得的
谢谢大家阅读本文,也希望大家能提出宝贵的意见,晚安

你可能感兴趣的:(.Net性能调优——类型揭秘)