泛型内存管理
本文关键字:管理 内存 泛型 | 更新日期: 2023-09-27 18:19:16
我有一个关于如何管理强类型泛型内存的问题
List<int> ints1 = new List<int>();
ints1.Add(1); ints1.Add(2); ints1.Add(3);
int[] ints2 = new int[10]();
ints2.Add(6); ints2.Add(7); ints2.Add(8);
问题1:我假设ints1是用一个新的关键字(new List<int>()
)初始化的,它成为一个引用类型。值1,2,3存储在内存中的位置(它们存储在堆栈中还是堆中)?
问题2:我知道List<int>
和int[]
之间的一个东西,List<int>
可以在运行时将其大小缩放到任何大小,这在int[]
中在编译时受到限制。所以,如果值1,2,3被存储在堆栈中,如果一个新的元素被添加到List<int>
中,比如4,它不会连续到前3位,那么ints1如何知道4的内存位置?
我假设ints1是用一个新的关键字
new List<int>()
初始化的,它变成了一个引用类型
这个假设是不正确的。你也可以在值类型上使用"new"关键字!
int x = new int();
使用"new"不会使任何东西成为引用类型。您可以对引用类型或值类型使用"new"。"new"表示将分配存储空间,并且将调用构造函数。
在值类型上使用"new"的情况下,分配的存储空间是临时存储空间。将对临时存储的引用传递给构造函数,然后将初始化后的结果复制到最终目的地(如果有的话)。("new"通常与赋值操作一起使用,但不一定)
在引用类型的情况下,存储分配两次:为实例分配长期存储,为对实例的长期存储的引用分配短期存储。引用被传递给构造函数,构造函数初始化长期存储。然后将引用从短期存储复制到最终目标(如果有的话)。
使List<int>
成为引用类型的原因是List<T>
被声明为类。
值1,2,3存储在内存中的位置(它们存储在堆栈中还是堆中)?
我们努力做一个内存管理器,让你不关心东西存储在哪里。值存储在短期内存池(作为堆栈或寄存器实现)或长期内存池(作为垃圾收集堆实现)中。根据该值的已知生存期分配存储。如果已知该值是短期值,则将其存储分配到短期池中。如果的值不是已知的短期值,则必须将其分配给长期池。
列表所拥有的1,2,3可以永远存在;我们不知道该列表是否会在当前激活框架之后继续存在。因此,存储1,2,3的内存被分配到长期池中。
不要相信"值类型总是在堆栈上分配"的谎言。显然,这是不可能的,因为包含数字的类或数组无法在当前堆栈帧中存活!值类型在池中分配,这对于它们已知的生命周期是有意义的。
List<int>
可以在运行时将其大小缩放为任何大小,而int[]
正确的。看看List<T>
是如何做到这一点的,这很有教育意义。它只是分配一个大于所需的数组。如果它发现它猜的太小,它会分配一个新的、更大的数组,并将旧数组的内容复制到新的数组中。List<T>
只是一堆数组副本的方便包装器!
如果值1,2,3被存储在堆栈中,并且一个新的元素4被添加到列表中,那么它将不连续到前三个。
正确的。这就是为什么值1,2,3的存储空间没有在堆栈上分配的原因之一。存储空间实际上是在堆上分配的一个数组。
那么列表如何知道第4项的内存位置呢?
list分配的数组太大。当您添加新项时,它会将其粘贴到过大的数组中未使用的空间中。当数组的空间用完时,它分配一个新的数组。
"new"语法用于初始化值类型和引用类型。新的列表在堆上创建;值在堆栈上加载(即在它们被添加到列表之前),但是一旦添加,它们就在堆上,在支撑列表的int[]
中。数组总是在堆上。
它们被复制到数组的事实也回答了第2部分的问题。数组大小过大,只有在满的时候才会重新分配。
注意;List<int>
不会"变成"引用类型;总是是引用类型
泛型(泛型集合)的内存管理与非泛型类型完全相同。
你的ints1
列表使用了一个数组。所以它是相同的ints2
(当它已被纠正)。在这两种情况下,堆上的一块内存都保存着int
的值。
List<>
类由一个数组、一个int Count
和一个int Capacity
属性组成。当你Add()一个元素Count
是递增的,当它传递给Capacity
时,一个新的数组被分配并且内容被复制。
问题1:http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx说:
List类是ArrayList类的泛型等价物。它使用大小为的数组实现IList泛型接口根据需要动态增加。
这看起来像一个简单的数组,它只是在溢出时重新分配。在每次重新分配时,大小都会翻倍——我研究过一次,但不记得是为了什么。
数组在托管堆上分配,就像您刚刚声明它一样。
-
List是一个引用类型,不管你怎么看它。所有这些类型都在堆上分配。我不知道c#编译器是否足够聪明,能够弄清楚在方法之外不使用的对象可以在堆栈上分配,(Eric Lippert可能会告诉我们,)但即使它做到了,这也是你作为程序员不需要担心的事情。这只是编译器为你做的一个优化,而你却没有注意到。
-
int类型的数组也是一个引用类型,它也是在堆上分配的,就这么简单。没有必要担心堆栈中存在一些假设的数组碎片,因为它们根本没有在堆栈中分配。
List<int> ints1 = new List<int>();
我假定ints1是用new关键字(new List())初始化的,它成为一个引用类型。
让我们先弄清楚一些术语:
-
ints1
不是任何类型的类型,而是变量,因此它永远不能"成为引用类型"。 - 变量具有静态("编译时")类型。例如,
ints1
被声明为List<int>
类型。变量的静态类型永远不会改变。 - 变量引用的值或对象的类型称为动态("运行时")类型。完成上述赋值后,
ints1
的动态类型为List<int>
。当一个新值或对象被赋值时,变量的动态类型可能会改变。
在ints1
的情况下,这两种类型恰好是相等的,但并不总是这样:
ICollection<int> ints3 = new List<int>();
这里,静态类型是ICollection<int>
(总是),动态类型是List<int>
(赋值之后)。
问题1的答案:
ints1.Add(1); ints1.Add(2); ints1.Add(3);
值1,2,3存储在内存中的位置(它们存储在堆栈中还是堆中)?
官方的答案是,这应该被认为是List<T>
的实现细节,不应该影响你。您只需要知道可以在List<T>
(如Add
、Clear
等)上执行的操作及其特性(如前置/后设条件、性能等)。
如果你仍然想知道这个类型是如何在内部工作的,让我们从这个提示开始:
List<T>
类[…]使用数组实现IList<T>
泛型接口,该数组的大小根据需要动态增加。本;MSDN参考页面List<T>
部分备注
意思是,您可以将List<T>
想象为T[]
类型的数组,它在需要时增加其容量。在ints1
的情况下,考虑int[]
。因为int
是一个值类型,所以列表中的具体值(1,2,3)将被就地存储。如果它是引用类型,则每个值将存储在单独的位置,并且数组将只包含引用。
请注意,我在上面的段落中没有提到术语"堆栈",也没有提到"堆"。这是因为这些概念是另一个实现细节,即。net平台的一个,您不应该太关心。(参见Eric Lippert的博客系列文章,"堆栈是一个实现细节",以及我对之前问题的回答,"在c++/c#/Java中,new总是在堆上分配吗?")
问题2的答案:
List可以在运行时将其大小缩放到任意大小,这在编译时受到int[]的限制。那么,如果值1 2 3存储在堆栈中,如果一个新的元素被添加到List中,比如说4,它不会连续到前3位,那么ints1如何知道4的内存位置呢?
同样,你不需要考虑这个。关于List<int>
,重要的是它提供的特定特性和操作是否符合您的目的。但如果我就此打住,我就不会回答你的问题了,所以让我们简单地思考一下:
当你谈论"堆栈"时,你可能是指正在执行的线程的调用堆栈。据我所知,使用这种数据结构的虚拟机和编程语言大多将其用于向函数/方法传递参数,也可能用于存储函数的局部值。这与说调用堆栈也用于局部变量不同,因为局部变量可能包含一个对象,该对象通过返回值或通过ref
/out
参数等返回给调用函数。
对我来说,在正常情况下,List<int>
对象似乎不太可能在调用堆栈上结束(顺便说一句,我不考虑stackalloc
或与unsafe
上下文相关的任何其他内容)。请记住,List<T>
是一个引用类型:虽然对实际对象的引用有可能(而且很有可能)在调用堆栈上结束,但对象的数据本身很可能不会。
IList<T>
的一个(可能是naïve)实现使用固定大小的数组进行项存储,当需要增加其容量时,可以动态分配一个新的更大的数组,然后将当前数组的内容复制到新数组中,然后放弃旧数组,转而使用新数组。
问题1
c#中的列表内部包含数组。List引用堆上的一个位置,该位置是一个存储所有值的数组。所以这些值存储在堆上。数组也是一样,如果它是类的一部分。
问题2
堆栈是连续的,因此在堆栈上压入另一个int将意味着它的内存地址是前一个int + 4的位置。当您添加项时,列表的工作方式是创建一个比您需要的更大的数组。当你达到数组的长度时,有一个算法会创建一个更大的数组,并复制当前的值。
另一个你可能感兴趣的东西是链表。链表内部不处理数组,而是处理节点。每个节点包含数据和列表中下一个节点的位置。双链表包含所有节点,以及列表中前一个节点的位置。