六个重要的.NET概念:栈、堆、值类型、引用类型、装箱和拆箱

目录

介绍

当你声明一个变量时,里面有什么?

栈和堆

值类型和引用类型

那么哪些数据类型是引用类型,哪些是值类型?

装箱和拆箱

装箱和拆箱的性能含义

关于源代码



介绍

本文将解释六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱。本文开始解释当您声明一个变量时内部会发生什么,然后继续解释两个重要的概念:栈和堆。然后,本文讨论了引用类型和值类型,并阐明了围绕它们的一些重要基础知识。

本文最后通过示例代码演示了装箱和拆箱对性能的影响。

图片取自http://michaelbungartz.wordpress.com/


当你声明一个变量时,里面有什么?

当您在.NET应用程序中声明一个变量时,它会在RAM中分配一些内存块。这个内存有三样东西:变量名、变量的数据类型和变量的值。

这是对内存中发生的事情的简单解释,但是根据数据类型,您的变量会分配到该类型的内存。内存分配有两种类型:栈内存和堆内存。在接下来的部分中,我们将尝试更详细地了解这两种类型的内存。


栈和堆

为了理解栈和堆,让我们了解下面代码内部实际发生的事情。

public void Method1()
{
    // Line 1
    int i=4;

    // Line 2
    int y=2;

    //Line 3
    class1 cls1 = new class1();
}

这是一个三行代码,让我们一行一行地了解内部是如何执行的。

内存分配和取消分配是使用LIFO(后进先出)逻辑完成的。换句话说,仅在内存的一端,即栈顶部分配和取消分配内存。

  • 1行:执行此行时,编译器会在栈中分配少量内存。栈负责跟踪应用程序所需的运行内存。
  • 2 :现在执行移动到下一步。顾名思义,栈是将这个内存分配堆叠在第一个内存分配之上。您可以将栈视为一系列放在彼此顶部的隔间或盒子。
  • 3行:在第3行中,我们创建了一个对象。执行此行时,它会在栈上创建一个指针,实际对象存储在称为的不同类型的内存位置中。不跟踪运行内存,它只是一堆可以随时访问的对象。堆用于动态内存分配。

这里要注意的更重要的一点是引用指针是在栈上分配的。该语句Class1 cls1;不为Class1的实例分配内存,它只分配一个栈变量cls1(并将其设置为null)。它运行到new关键字的时候,它在上分配。

退出方法(fun:现在终于执行控制开始退出方法。当它通过结束控制时,它清除所有分配在栈上的内存变量。换句话说,所有与int数据类型相关的变量都以后进先出方式从栈中解除分配。

捕获——它没有解除分配堆内存。垃圾收集器稍后将取消分配此内存。

现在很多开发者朋友一定很疑惑,为什么会有两种类型的内存,难道我们不能把所有的东西都分配到一种内存类型上就完成了吗?

如果仔细观察,原始数据类型并不复杂,它们包含像int i = 0这样的单个值。对象数据类型很复杂,它们引用其他对象或其他原始数据类型。换句话说,它们持有对其他多个值的引用,并且每个值都必须存储在内存中。对象类型需要动态内存,而原始类型需要静态类型内存。如果需要动态内存,则将其分配在堆上,否则将进入栈。

图片取自http://michaelbungartz.wordpress.com/


值类型和引用类型

现在我们已经了解了栈和堆的概念,是时候了解值类型和引用类型的概念了。值类型是将数据和内存保存在同一位置的类型。引用类型有一个指向内存位置的指针。

下面是一个具有名称为i的简单整数数据类型,其值被分配给另一个具有名称j的整数数据类型。这两个内存值都分配在栈上。

当我们将int值分配给另一个int值时,它会创建一个完全不同的副本。换句话说,如果你改变其中一个,另一个不会改变。这些类型的数据类型称为值类型

当我们创建一个对象并将一个对象分配给另一个对象时,它们都指向相同的内存位置,如下面的代码片段所示。所以当我们赋值objobj1时,它们都指向同一个内存位置。

换句话说,如果我们改变其中一个,另一个对象也会受到影响;这被称为引用类型


那么哪些数据类型是引用类型,哪些是值类型?

.NET中,根据数据类型,变量要么在栈上分配,要么在堆上分配。'String''Objects'是引用类型,任何其他.NET原始数据类型都在栈上分配。下图以更详细的方式解释了相同的内容。


装箱和拆箱

哇,你传授了这么多知识,那么在实际编程中又有什么用呢?最大的影响之一是了解由于数据从栈移动到堆(反之亦然)而导致的性能损失。

考虑下面的代码片段。当我们将值类型移动到引用类型时,数据从栈移动到堆。当我们将引用类型移动到值类型时,数据从堆移动到栈。

这种从堆到栈的数据移动,反之亦然,这会导致性能下降。

当数据从值类型移动到引用类型时,它被称为装箱,反之则被称为拆箱

如果您编译上述代码并在ILDASM中看到相同的内容,您可以在IL代码中看到装箱拆箱的样子。下图演示了相同的内容:


装箱和拆箱的性能含义

为了查看性能如何受到影响,我们运行了以下两个函数10,000次。一个函数有装箱,另一个函数很简单。我们使用秒表对象来监控所用的时间。

装箱函数在3542毫秒内执行,而没有装箱时,代码在2477毫秒内执行。换句话说,尽量避免装箱和拆箱。在需要装箱和拆箱的项目中,在绝对必要时使用它。

本文附带了示例代码,演示了这种性能影响。

目前,我还没有包括拆箱的源代码,但同样适用于它。您可以使用stopwatch类编写代码并对其进行试验。


关于源代码

随文章附有一个简单的代码,它演示了装箱如何产生性能影响。您可以在此处下载源代码。

Six Important .NET Concepts: Stack, Heap, Value Types, Reference Types, Boxing, and Unboxing - CodeProject

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值