直白点儿说:值类型就是现金,要用直接用;引用类型是存折,要用还得先去银行取现。
声明一个值类型变量,编译器会在栈上分配一个空间,这个空间对应着该值类型变量,空间里存储的就是该变量的值。引用类型的实例分配在堆上,新建一个引用类型实例,得到的变量值对应的是该实例的内存分配地址,这就像您的银行账号一样。
举一个简单的例子,我们有一个名为Point2D的对象,用来表示二维空间中的坐标,每一个坐标值x,y都用一个short类型表示,整个对象占4个字节。现在假设我们需要在内存中存储1000万个这样的坐标点集合对象。那么他们会占用多大内存呢?这个问题的答案其实在很大程度上依赖Point2D是值类型还是引用类型。如果他是引用类型,1000万个这样的点的集合实际上是保存的对这1000万个点的引用。在一个32位的系统上,光存储对这十万个点的引用就要占用将近40MB的内存。对象本身也要占用同样大小的内存。实际上,如果我们较真的话,每一个Point2D实例对象要占用12个字节(同步块索引,对象类型指针,实体),使得要存储10万个这样的对象需要将近160MB的内存。但是,如果Point2D是一个值类型,1000万个这样的点的集合就存储的是1000万个对象的实例,没有浪费一个字节的内存空间,总共只需要占用40MB的内存。这比使用引用类型将近少了四分之一,内存的密度在某些情况下使得我们偏爱使用值类型。
存储实际的点数据值比存储引用还有一个好处,那就是如果你想遍历一个存储了很多该类型的对象,编译器和硬件能够很容易的遍历值类型对象,因为他们在内存中是连续分配的,而引用类型则不同,在托管堆(heap)上的对象不一定在内存中是连续分配的。对于值类型集合对象的话,CPU的缓存机制可以对连续对象进行更快的读取。
时间: 2024-12-30 03:48:30