数组、堆和堆栈和值类型。

时间:2021-09-06 22:02:43
int[] myIntegers;
myIntegers = new int[100];

In the above code, is new int[100] generating the array on the heap? From what I've read on CLR via c#, the answer is yes. But what I can't understand, is what happens to the actual int's inside the array. As they are value types, I'd guess they'd have to be boxed, as I can, for example, pass myIntegers to other parts of the program and it'd clutter up the stack if they were left on it all the time. Or am I wrong? I'd guess they'd just be boxed and would live on the heap for as long the array existed.

在上面的代码中,新int[100]是否生成堆上的数组?从我通过c#在CLR上读到的内容来看,答案是肯定的。但我不明白的是,数组中实际的int数会发生什么。由于它们是值类型,我猜它们必须被装箱,例如,我可以将myIntegers传递给程序的其他部分,如果它们一直留在堆栈上,它会使堆栈变得混乱。还是我错了?我猜它们会被装箱,并且在这个数组存在的时候就会在堆上生存。

8 个解决方案

#1


248  

Your array is allocated on the heap, and the ints are not boxed.

数组被分配到堆上,而ints没有被装箱。

The source of your confusion is likely because people have said that reference types are allocated on the heap and value types are allocated on the stack. This is not an entirely accurate representation.

造成混乱的原因可能是人们说引用类型被分配到堆上,值类型被分配到堆栈上。这不是一个完全准确的表述。

All local variables and parameters are allocated on the stack. This includes both value types and reference types. The difference between the two is only what is stored in the variable. Unsurprisingly, for a value type, the value of the type is stored directly in the variable, and for a reference type, the value of the type is stored on the heap, and a reference to this value is what is stored in the variable.

所有本地变量和参数都被分配到堆栈上。这包括值类型和引用类型。两者之间的区别仅仅是变量中存储的内容。不出所料,对于值类型,类型的值直接存储在变量中,对于引用类型,类型的值存储在堆中,对该值的引用存储在变量中。

The same holds true for fields. When memory is allocated for an instance of an aggregate type (a class or a struct), it must include storage for each of its instance fields. For reference-type fields, this storage holds just a reference to the value, which would itself be allocated on the heap later. For value-type fields, this storage holds the actual value.

字段也是如此。当为聚合类型(类或结构)的实例分配内存时,它必须包含每个实例字段的存储。对于引用类型字段,此存储仅保存对值的引用,该值本身稍后将被分配到堆上。对于值类型字段,此存储持有实际值。

So, given the following types:

因此,鉴于以下类型:

class RefType{
    public int    I;
    public string S;
    public long   L;
}

struct ValType{
    public int    I;
    public string S;
    public long   L;
}

The values of each of these types would require 16 bytes of memory (assuming a 32-bit word size). The field I in each case takes 4 bytes to store its value, the field S takes 4 bytes to store its reference, and the field L takes 8 bytes to store its value. So the memory for the value of both RefType and ValType looks like this:

每种类型的值都需要16字节的内存(假设32位字大小)。每个例子中的字段I需要4个字节来存储它的值,字段S需要4个字节来存储它的引用,字段L需要8个字节来存储它的值。所以RefType和ValType的值的内存是这样的:

 0 ┌───────────────────┐
   │        I          │
 4 ├───────────────────┤
   │        S          │
 8 ├───────────────────┤
   │        L          │
   │                   │
16 └───────────────────┘

Now if you had three local variables in a function, of types RefType, ValType, and int[], like this:

如果函数中有三个局部变量,它们是RefType, ValType和int[],就像这样:

RefType refType;
ValType valType;
int[]   intArray;

then your stack might look like this:

那么你的堆栈可能是这样的:

 0 ┌───────────────────┐
   │     refType       │
 4 ├───────────────────┤
   │     valType       │
   │                   │
   │                   │
   │                   │
20 ├───────────────────┤
   │     intArray      │
24 └───────────────────┘

If you assigned values to these local variables, like so:

如果你给这些局部变量赋值,比如:

refType = new RefType();
refType.I = 100;
refType.S = "refType.S";
refType.L = 0x0123456789ABCDEF;

valType = new ValType();
valType.I = 200;
valType.S = "valType.S";
valType.L = 0x0011223344556677;

intArray = new int[4];
intArray[0] = 300;
intArray[1] = 301;
intArray[2] = 302;
intArray[3] = 303;

Then your stack might look something like this:

那么你的堆栈可能是这样的:

 0 ┌───────────────────┐
   │    0x4A963B68     │ -- heap address of `refType`
 4 ├───────────────────┤
   │       200         │ -- value of `valType.I`
   │    0x4A984C10     │ -- heap address of `valType.S`
   │    0x44556677     │ -- low 32-bits of `valType.L`
   │    0x00112233     │ -- high 32-bits of `valType.L`
20 ├───────────────────┤
   │    0x4AA4C288     │ -- heap address of `intArray`
24 └───────────────────┘

Memory at address 0x4A963B68 (value of refType) would be something like:

地址0x4A963B68 (refType的值)的内存应该是:

 0 ┌───────────────────┐
   │       100         │ -- value of `refType.I`
 4 ├───────────────────┤
   │    0x4A984D88     │ -- heap address of `refType.S`
 8 ├───────────────────┤
   │    0x89ABCDEF     │ -- low 32-bits of `refType.L`
   │    0x01234567     │ -- high 32-bits of `refType.L`
16 └───────────────────┘

Memory at address 0x4AA4C288 (value of intArray) would be something like:

地址0x4AA4C288 (intArray值)的内存如下:

 0 ┌───────────────────┐
   │        4          │ -- length of array
 4 ├───────────────────┤
   │       300         │ -- `intArray[0]`
 8 ├───────────────────┤
   │       301         │ -- `intArray[1]`
12 ├───────────────────┤
   │       302         │ -- `intArray[2]`
16 ├───────────────────┤
   │       303         │ -- `intArray[3]`
20 └───────────────────┘

Now if you passed intArray to another function, the value pushed onto the stack would be 0x4AA4C288, the address of the array, not a copy of the array.

现在,如果将intArray传递给另一个函数,将其推到堆栈上的值将是0x4AA4C288,该数组的地址,而不是数组的副本。

#2


21  

Yes the array will be located on the heap.

是的,数组将位于堆上。

The ints inside the array will not be boxed. Just because a value type exists on the heap, does not necessarily mean it will be boxed. Boxing will only occur when a value type, such as int, is assigned to a reference of type object.

数组中的ints将不会被装箱。仅仅因为堆上存在值类型,并不一定意味着它将被装箱。只有当将值类型(如int)分配给类型对象的引用时,才会发生装箱。

For example

例如

Does not box:

没有盒子:

int i = 42;
myIntegers[0] = 42;

Boxes:

箱:

object i = 42;
object[] arr = new object[10];  // no boxing here 
arr[0] = 42;

You may also want to check out Eric's post on this subject:

你也可以看看Eric关于这个主题的文章:

#3


14  

To understand what's happening, here are some facts:

为了了解发生了什么,以下是一些事实:

  • Object are always allocated on the heap.
  • 对象总是在堆上分配。
  • The heap only contains objects.
  • 堆只包含对象。
  • Value types are either allocated on the stack, or part of an object on the heap.
  • 值类型可以在堆栈上分配,也可以是堆上对象的一部分。
  • An array is an object.
  • 数组是对象。
  • An array can only contain value types.
  • 数组只能包含值类型。
  • An object reference is a value type.
  • 对象引用是一个值类型。

So, if you have an array of integers, the array is allocated on the heap and the integers that it contains is part of the array object on the heap. The integers reside inside the array object on the heap, not as separate objects, so they are not boxed.

所以,如果你有一个整数数组,数组被分配到堆上,它包含的整数是堆上数组对象的一部分。这些整数驻留在堆上的数组对象中,而不是作为单独的对象,因此它们不被装箱。

If you have an array of strings, it's really an array of string references. As references are value types they will be part of the array object on the heap. If you put a string object in the array, you actually put the reference to the string object in the array, and the string is a separate object on the heap.

如果你有一个字符串数组,它实际上是一个字符串引用数组。由于引用是值类型,它们将成为堆上数组对象的一部分。如果在数组中放置一个string对象,那么实际上是将对string对象的引用放在数组中,并且该字符串是堆上的一个单独的对象。

#4


8  

I think at the core of your question lies a misunderstanding about reference and value types. This is something probably every .NET and Java developer struggled with.

我认为你问题的核心在于对参考和价值类型的误解。这可能是每个。net和Java开发人员都在努力解决的问题。

An array is just a list of values. If it's an array of a reference type (say a string[]) then the array is a list of references to various string objects on the heap, as a reference is the value of a reference type. Internally, these references are implemented as pointers to an address in memory. If you wish to visualize this, such an array would look like this in memory (on the heap):

数组只是一个值列表。如果它是引用类型的数组(比如string[]),那么该数组就是对堆上各种字符串对象的引用列表,因为引用是引用类型的值。在内部,这些引用被实现为指向内存中的地址的指针。如果您希望将其可视化,那么这样的数组在内存(堆上)中应该是这样的:

[ 00000000, 00000000, 00000000, F8AB56AA ]

[00000000, 00000000, 00000000, F8AB56AA]

This is an array of string that contains 4 references to string objects on the heap (the numbers here are hexadecimal). Currently, only the last string actually points to anything (memory is initialized to all zero's when allocated), this array would basically be the result of this code in C#:

这是一个字符串数组,其中包含对堆上字符串对象的4个引用(这里的数字是十六进制)。目前,只有最后一个字符串实际指向任何东西(分配时内存初始化为0),这个数组基本上是c#代码的结果:

string[] strings = new string[4];
strings[3] = "something"; // the string was allocated at 0xF8AB56AA by the CLR

The above array would be in a 32 bit program. In a 64 bit program, the references would be twice as big (F8AB56AA would be 00000000F8AB56AA).

上面的数组将在一个32位的程序中。在64位程序中,引用将是原来的两倍(F8AB56AA将是00000000F8AB56AA)。

If you have an array of value types (say an int[]) then the array is a list of integers, as the value of a value type is the value itself (hence the name). The visualization of such an array would be this:

如果您有一个值类型数组(比如int[]),那么数组就是一个整数的列表,因为值类型的值就是值本身(即名称)。这种阵列的可视化将是:

[ 00000000, 45FF32BB, 00000000, 00000000 ]

[00000000, 45FF32BB, 00000000, 00000000]

This is an array of 4 integers, where only the second int is assigned a value (to 1174352571, which is the decimal representation of that hexadecimal number) and the rest of the integers would be 0 (like I said, memory is initialized to zero and 00000000 in hexadecimal is 0 in decimal). The code that produced this array would be:

这是4的整数数组,第二个int被分配一个值(1174352571,十六进制数的小数表示)和其余的整数是0(就像我说的,内存被初始化为零,00000000在十进制十六进制是0)。生成这个数组的代码是:

 int[] integers = new int[4];
 integers[1] = 1174352571; // integers[1] = 0x45FF32BB would be valid too

This int[] array would also be stored on the heap.

这个int[]数组也将存储在堆中。

As another example, the memory of a short[4] array would look like this:

另一个示例是,短[4]数组的内存如下所示:

[ 0000, 0000, 0000, 0000 ]

[0000, 0000, 0000]

As the value of a short is a 2 byte number.

因为短的值是一个2字节的数字。

Where a value type is stored, is just an implementation detail as Eric Lippert explains very well here, not inherent to the differences between value and reference types (which is difference in behavior).

值类型存储在哪里,就像Eric Lippert在这里很好地解释的那样,只是实现细节,而不是值和引用类型之间的差异(行为上的差异)所固有的。

When you pass something to a method (be that a reference type or a value type) then a copy of the value of the type is actually passed to the method. In the case of a reference type, the value is a reference (think of this as a pointer to a piece of memory, although that also is an implementation detail) and in the case of a value type, the value is the thing itself.

当向方法传递某些内容时(即引用类型或值类型),则该类型的值的副本实际上已传递给该方法。在引用类型的情况下,值是一个引用(把它当作一个指向内存块的指针,尽管这也是一个实现细节),在值类型的情况下,值就是它本身。

// Calling this method creates a copy of the *reference* to the string
// and a copy of the int itself, so copies of the *values*
void SomeMethod(string s, int i){}

Boxing only occurs if you convert a value type to a reference type. This code boxes:

只有将值类型转换为引用类型时才会发生装箱。这段代码框:

object o = 5;

#5


1  

An array of integers is allocated on the heap, nothing more, nothing less. myIntegers references to the start of the section where the ints are allocated. That reference is located on the stack.

在堆上分配一个整数数组,不多也不少。myIntegers引用了分配int的部分的开始。该引用位于堆栈上。

If you have a array of reference type objects, like the Object type, myObjects[], located on the stack, would reference to the bunch of values which reference the objects themselfes.

如果您有一个引用类型对象数组,比如对象类型myObjects[],位于堆栈上,它将引用引用引用对象本身的一系列值。

To summarize, if you pass myIntegers to some functions, you only pass the reference to the place where the real bunch of integers is allocated.

总而言之,如果您将myinteger传递给一些函数,您只会将引用传递到分配真正的整数部分的地方。

#6


1  

There is no boxing in your example code.

在您的示例代码中没有装箱。

Value types can live on the heap as they do in your array of ints. The array is allocated on the heap and it stores ints, which happen to be value types. The contents of the array are initialized to default(int), which happens to be zero.

值类型可以驻留在堆上,就像它们在int数组中所做的那样。数组被分配到堆上并存储ints,而ints恰好是值类型。数组的内容被初始化为缺省值(int),它恰好为零。

Consider a class that contains a value type:

考虑一个包含值类型的类:


    class HasAnInt
    {
        int i;
    }

    HasAnInt h = new HasAnInt();

Variable h refers to an instance of HasAnInt that lives on the heap. It just happens to contain a value type. That's perfectly okay, 'i' just happens to live on the heap as it's contained in a class. There is no boxing in this example either.

变量h指的是位于堆上的HasAnInt实例。它刚好包含一个值类型。这完全没问题,'i'刚好位于堆上,因为它包含在类中。在这个例子中也没有装箱。

#7


1  

Enough has been said by everybody, but if someone is looking for a clear (but non-official) sample and documentation about heap, stack, local variables, and static variables, refer the complete Jon Skeet's article on Memory in .NET - what goes where

每个人都说得够多了,但是如果有人正在寻找关于堆、堆栈、本地变量和静态变量的清晰(但非官方的)示例和文档,请参考Jon Skeet在.NET中关于内存的完整文章——到哪里去了

Excerpt:

摘录:

  1. Each local variable (ie one declared in a method) is stored on the stack. That includes reference type variables - the variable itself is on the stack, but remember that the value of a reference type variable is only a reference (or null), not the object itself. Method parameters count as local variables too, but if they are declared with the ref modifier, they don't get their own slot, but share a slot with the variable used in the calling code. See my article on parameter passing for more details.

    每个局部变量(即方法中声明的变量)都存储在堆栈中。这包括引用类型变量——变量本身在堆栈上,但是请记住引用类型变量的值只是引用(或null),而不是对象本身。方法参数也算作局部变量,但是如果用ref修饰符声明它们,它们不会得到自己的槽,而是与调用代码中使用的变量共享一个槽。查看我关于参数传递的文章,了解更多细节。

  2. Instance variables for a reference type are always on the heap. That's where the object itself "lives".

    引用类型的实例变量总是在堆上。这就是物体本身“存在”的地方。

  3. Instance variables for a value type are stored in the same context as the variable that declares the value type. The memory slot for the instance effectively contains the slots for each field within the instance. That means (given the previous two points) that a struct variable declared within a method will always be on the stack, whereas a struct variable which is an instance field of a class will be on the heap.

    值类型的实例变量与声明值类型的变量存储在同一个上下文中。实例的内存槽有效地包含实例中每个字段的槽。这意味着(给定前两点)在方法中声明的struct变量将始终位于堆栈上,而作为类实例字段的struct变量将位于堆上。

  4. Every static variable is stored on the heap, regardless of whether it's declared within a reference type or a value type. There is only one slot in total no matter how many instances are created. (There don't need to be any instances created for that one slot to exist though.) The details of exactly which heap the variables live on are complicated, but explained in detail in an MSDN article on the subject.

    每个静态变量都存储在堆中,不管它是在引用类型中声明的,还是在值类型中声明的。无论创建多少实例,总共只有一个槽。(不过,不需要为这个插槽创建任何实例。)具体哪个堆的详细信息是复杂的,但是在MSDN的文章中详细解释了这个主题。

#8


0  

These are illustrations depicting above answer by @P Daddy

以上是@P Daddy给出的解答

数组、堆和堆栈和值类型。

数组、堆和堆栈和值类型。

And I illustrated the corresponding contents in my style.

我用我的风格说明了相应的内容。

数组、堆和堆栈和值类型。

#1


248  

Your array is allocated on the heap, and the ints are not boxed.

数组被分配到堆上,而ints没有被装箱。

The source of your confusion is likely because people have said that reference types are allocated on the heap and value types are allocated on the stack. This is not an entirely accurate representation.

造成混乱的原因可能是人们说引用类型被分配到堆上,值类型被分配到堆栈上。这不是一个完全准确的表述。

All local variables and parameters are allocated on the stack. This includes both value types and reference types. The difference between the two is only what is stored in the variable. Unsurprisingly, for a value type, the value of the type is stored directly in the variable, and for a reference type, the value of the type is stored on the heap, and a reference to this value is what is stored in the variable.

所有本地变量和参数都被分配到堆栈上。这包括值类型和引用类型。两者之间的区别仅仅是变量中存储的内容。不出所料,对于值类型,类型的值直接存储在变量中,对于引用类型,类型的值存储在堆中,对该值的引用存储在变量中。

The same holds true for fields. When memory is allocated for an instance of an aggregate type (a class or a struct), it must include storage for each of its instance fields. For reference-type fields, this storage holds just a reference to the value, which would itself be allocated on the heap later. For value-type fields, this storage holds the actual value.

字段也是如此。当为聚合类型(类或结构)的实例分配内存时,它必须包含每个实例字段的存储。对于引用类型字段,此存储仅保存对值的引用,该值本身稍后将被分配到堆上。对于值类型字段,此存储持有实际值。

So, given the following types:

因此,鉴于以下类型:

class RefType{
    public int    I;
    public string S;
    public long   L;
}

struct ValType{
    public int    I;
    public string S;
    public long   L;
}

The values of each of these types would require 16 bytes of memory (assuming a 32-bit word size). The field I in each case takes 4 bytes to store its value, the field S takes 4 bytes to store its reference, and the field L takes 8 bytes to store its value. So the memory for the value of both RefType and ValType looks like this:

每种类型的值都需要16字节的内存(假设32位字大小)。每个例子中的字段I需要4个字节来存储它的值,字段S需要4个字节来存储它的引用,字段L需要8个字节来存储它的值。所以RefType和ValType的值的内存是这样的:

 0 ┌───────────────────┐
   │        I          │
 4 ├───────────────────┤
   │        S          │
 8 ├───────────────────┤
   │        L          │
   │                   │
16 └───────────────────┘

Now if you had three local variables in a function, of types RefType, ValType, and int[], like this:

如果函数中有三个局部变量,它们是RefType, ValType和int[],就像这样:

RefType refType;
ValType valType;
int[]   intArray;

then your stack might look like this:

那么你的堆栈可能是这样的:

 0 ┌───────────────────┐
   │     refType       │
 4 ├───────────────────┤
   │     valType       │
   │                   │
   │                   │
   │                   │
20 ├───────────────────┤
   │     intArray      │
24 └───────────────────┘

If you assigned values to these local variables, like so:

如果你给这些局部变量赋值,比如:

refType = new RefType();
refType.I = 100;
refType.S = "refType.S";
refType.L = 0x0123456789ABCDEF;

valType = new ValType();
valType.I = 200;
valType.S = "valType.S";
valType.L = 0x0011223344556677;

intArray = new int[4];
intArray[0] = 300;
intArray[1] = 301;
intArray[2] = 302;
intArray[3] = 303;

Then your stack might look something like this:

那么你的堆栈可能是这样的:

 0 ┌───────────────────┐
   │    0x4A963B68     │ -- heap address of `refType`
 4 ├───────────────────┤
   │       200         │ -- value of `valType.I`
   │    0x4A984C10     │ -- heap address of `valType.S`
   │    0x44556677     │ -- low 32-bits of `valType.L`
   │    0x00112233     │ -- high 32-bits of `valType.L`
20 ├───────────────────┤
   │    0x4AA4C288     │ -- heap address of `intArray`
24 └───────────────────┘

Memory at address 0x4A963B68 (value of refType) would be something like:

地址0x4A963B68 (refType的值)的内存应该是:

 0 ┌───────────────────┐
   │       100         │ -- value of `refType.I`
 4 ├───────────────────┤
   │    0x4A984D88     │ -- heap address of `refType.S`
 8 ├───────────────────┤
   │    0x89ABCDEF     │ -- low 32-bits of `refType.L`
   │    0x01234567     │ -- high 32-bits of `refType.L`
16 └───────────────────┘

Memory at address 0x4AA4C288 (value of intArray) would be something like:

地址0x4AA4C288 (intArray值)的内存如下:

 0 ┌───────────────────┐
   │        4          │ -- length of array
 4 ├───────────────────┤
   │       300         │ -- `intArray[0]`
 8 ├───────────────────┤
   │       301         │ -- `intArray[1]`
12 ├───────────────────┤
   │       302         │ -- `intArray[2]`
16 ├───────────────────┤
   │       303         │ -- `intArray[3]`
20 └───────────────────┘

Now if you passed intArray to another function, the value pushed onto the stack would be 0x4AA4C288, the address of the array, not a copy of the array.

现在,如果将intArray传递给另一个函数,将其推到堆栈上的值将是0x4AA4C288,该数组的地址,而不是数组的副本。

#2


21  

Yes the array will be located on the heap.

是的,数组将位于堆上。

The ints inside the array will not be boxed. Just because a value type exists on the heap, does not necessarily mean it will be boxed. Boxing will only occur when a value type, such as int, is assigned to a reference of type object.

数组中的ints将不会被装箱。仅仅因为堆上存在值类型,并不一定意味着它将被装箱。只有当将值类型(如int)分配给类型对象的引用时,才会发生装箱。

For example

例如

Does not box:

没有盒子:

int i = 42;
myIntegers[0] = 42;

Boxes:

箱:

object i = 42;
object[] arr = new object[10];  // no boxing here 
arr[0] = 42;

You may also want to check out Eric's post on this subject:

你也可以看看Eric关于这个主题的文章:

#3


14  

To understand what's happening, here are some facts:

为了了解发生了什么,以下是一些事实:

  • Object are always allocated on the heap.
  • 对象总是在堆上分配。
  • The heap only contains objects.
  • 堆只包含对象。
  • Value types are either allocated on the stack, or part of an object on the heap.
  • 值类型可以在堆栈上分配,也可以是堆上对象的一部分。
  • An array is an object.
  • 数组是对象。
  • An array can only contain value types.
  • 数组只能包含值类型。
  • An object reference is a value type.
  • 对象引用是一个值类型。

So, if you have an array of integers, the array is allocated on the heap and the integers that it contains is part of the array object on the heap. The integers reside inside the array object on the heap, not as separate objects, so they are not boxed.

所以,如果你有一个整数数组,数组被分配到堆上,它包含的整数是堆上数组对象的一部分。这些整数驻留在堆上的数组对象中,而不是作为单独的对象,因此它们不被装箱。

If you have an array of strings, it's really an array of string references. As references are value types they will be part of the array object on the heap. If you put a string object in the array, you actually put the reference to the string object in the array, and the string is a separate object on the heap.

如果你有一个字符串数组,它实际上是一个字符串引用数组。由于引用是值类型,它们将成为堆上数组对象的一部分。如果在数组中放置一个string对象,那么实际上是将对string对象的引用放在数组中,并且该字符串是堆上的一个单独的对象。

#4


8  

I think at the core of your question lies a misunderstanding about reference and value types. This is something probably every .NET and Java developer struggled with.

我认为你问题的核心在于对参考和价值类型的误解。这可能是每个。net和Java开发人员都在努力解决的问题。

An array is just a list of values. If it's an array of a reference type (say a string[]) then the array is a list of references to various string objects on the heap, as a reference is the value of a reference type. Internally, these references are implemented as pointers to an address in memory. If you wish to visualize this, such an array would look like this in memory (on the heap):

数组只是一个值列表。如果它是引用类型的数组(比如string[]),那么该数组就是对堆上各种字符串对象的引用列表,因为引用是引用类型的值。在内部,这些引用被实现为指向内存中的地址的指针。如果您希望将其可视化,那么这样的数组在内存(堆上)中应该是这样的:

[ 00000000, 00000000, 00000000, F8AB56AA ]

[00000000, 00000000, 00000000, F8AB56AA]

This is an array of string that contains 4 references to string objects on the heap (the numbers here are hexadecimal). Currently, only the last string actually points to anything (memory is initialized to all zero's when allocated), this array would basically be the result of this code in C#:

这是一个字符串数组,其中包含对堆上字符串对象的4个引用(这里的数字是十六进制)。目前,只有最后一个字符串实际指向任何东西(分配时内存初始化为0),这个数组基本上是c#代码的结果:

string[] strings = new string[4];
strings[3] = "something"; // the string was allocated at 0xF8AB56AA by the CLR

The above array would be in a 32 bit program. In a 64 bit program, the references would be twice as big (F8AB56AA would be 00000000F8AB56AA).

上面的数组将在一个32位的程序中。在64位程序中,引用将是原来的两倍(F8AB56AA将是00000000F8AB56AA)。

If you have an array of value types (say an int[]) then the array is a list of integers, as the value of a value type is the value itself (hence the name). The visualization of such an array would be this:

如果您有一个值类型数组(比如int[]),那么数组就是一个整数的列表,因为值类型的值就是值本身(即名称)。这种阵列的可视化将是:

[ 00000000, 45FF32BB, 00000000, 00000000 ]

[00000000, 45FF32BB, 00000000, 00000000]

This is an array of 4 integers, where only the second int is assigned a value (to 1174352571, which is the decimal representation of that hexadecimal number) and the rest of the integers would be 0 (like I said, memory is initialized to zero and 00000000 in hexadecimal is 0 in decimal). The code that produced this array would be:

这是4的整数数组,第二个int被分配一个值(1174352571,十六进制数的小数表示)和其余的整数是0(就像我说的,内存被初始化为零,00000000在十进制十六进制是0)。生成这个数组的代码是:

 int[] integers = new int[4];
 integers[1] = 1174352571; // integers[1] = 0x45FF32BB would be valid too

This int[] array would also be stored on the heap.

这个int[]数组也将存储在堆中。

As another example, the memory of a short[4] array would look like this:

另一个示例是,短[4]数组的内存如下所示:

[ 0000, 0000, 0000, 0000 ]

[0000, 0000, 0000]

As the value of a short is a 2 byte number.

因为短的值是一个2字节的数字。

Where a value type is stored, is just an implementation detail as Eric Lippert explains very well here, not inherent to the differences between value and reference types (which is difference in behavior).

值类型存储在哪里,就像Eric Lippert在这里很好地解释的那样,只是实现细节,而不是值和引用类型之间的差异(行为上的差异)所固有的。

When you pass something to a method (be that a reference type or a value type) then a copy of the value of the type is actually passed to the method. In the case of a reference type, the value is a reference (think of this as a pointer to a piece of memory, although that also is an implementation detail) and in the case of a value type, the value is the thing itself.

当向方法传递某些内容时(即引用类型或值类型),则该类型的值的副本实际上已传递给该方法。在引用类型的情况下,值是一个引用(把它当作一个指向内存块的指针,尽管这也是一个实现细节),在值类型的情况下,值就是它本身。

// Calling this method creates a copy of the *reference* to the string
// and a copy of the int itself, so copies of the *values*
void SomeMethod(string s, int i){}

Boxing only occurs if you convert a value type to a reference type. This code boxes:

只有将值类型转换为引用类型时才会发生装箱。这段代码框:

object o = 5;

#5


1  

An array of integers is allocated on the heap, nothing more, nothing less. myIntegers references to the start of the section where the ints are allocated. That reference is located on the stack.

在堆上分配一个整数数组,不多也不少。myIntegers引用了分配int的部分的开始。该引用位于堆栈上。

If you have a array of reference type objects, like the Object type, myObjects[], located on the stack, would reference to the bunch of values which reference the objects themselfes.

如果您有一个引用类型对象数组,比如对象类型myObjects[],位于堆栈上,它将引用引用引用对象本身的一系列值。

To summarize, if you pass myIntegers to some functions, you only pass the reference to the place where the real bunch of integers is allocated.

总而言之,如果您将myinteger传递给一些函数,您只会将引用传递到分配真正的整数部分的地方。

#6


1  

There is no boxing in your example code.

在您的示例代码中没有装箱。

Value types can live on the heap as they do in your array of ints. The array is allocated on the heap and it stores ints, which happen to be value types. The contents of the array are initialized to default(int), which happens to be zero.

值类型可以驻留在堆上,就像它们在int数组中所做的那样。数组被分配到堆上并存储ints,而ints恰好是值类型。数组的内容被初始化为缺省值(int),它恰好为零。

Consider a class that contains a value type:

考虑一个包含值类型的类:


    class HasAnInt
    {
        int i;
    }

    HasAnInt h = new HasAnInt();

Variable h refers to an instance of HasAnInt that lives on the heap. It just happens to contain a value type. That's perfectly okay, 'i' just happens to live on the heap as it's contained in a class. There is no boxing in this example either.

变量h指的是位于堆上的HasAnInt实例。它刚好包含一个值类型。这完全没问题,'i'刚好位于堆上,因为它包含在类中。在这个例子中也没有装箱。

#7


1  

Enough has been said by everybody, but if someone is looking for a clear (but non-official) sample and documentation about heap, stack, local variables, and static variables, refer the complete Jon Skeet's article on Memory in .NET - what goes where

每个人都说得够多了,但是如果有人正在寻找关于堆、堆栈、本地变量和静态变量的清晰(但非官方的)示例和文档,请参考Jon Skeet在.NET中关于内存的完整文章——到哪里去了

Excerpt:

摘录:

  1. Each local variable (ie one declared in a method) is stored on the stack. That includes reference type variables - the variable itself is on the stack, but remember that the value of a reference type variable is only a reference (or null), not the object itself. Method parameters count as local variables too, but if they are declared with the ref modifier, they don't get their own slot, but share a slot with the variable used in the calling code. See my article on parameter passing for more details.

    每个局部变量(即方法中声明的变量)都存储在堆栈中。这包括引用类型变量——变量本身在堆栈上,但是请记住引用类型变量的值只是引用(或null),而不是对象本身。方法参数也算作局部变量,但是如果用ref修饰符声明它们,它们不会得到自己的槽,而是与调用代码中使用的变量共享一个槽。查看我关于参数传递的文章,了解更多细节。

  2. Instance variables for a reference type are always on the heap. That's where the object itself "lives".

    引用类型的实例变量总是在堆上。这就是物体本身“存在”的地方。

  3. Instance variables for a value type are stored in the same context as the variable that declares the value type. The memory slot for the instance effectively contains the slots for each field within the instance. That means (given the previous two points) that a struct variable declared within a method will always be on the stack, whereas a struct variable which is an instance field of a class will be on the heap.

    值类型的实例变量与声明值类型的变量存储在同一个上下文中。实例的内存槽有效地包含实例中每个字段的槽。这意味着(给定前两点)在方法中声明的struct变量将始终位于堆栈上,而作为类实例字段的struct变量将位于堆上。

  4. Every static variable is stored on the heap, regardless of whether it's declared within a reference type or a value type. There is only one slot in total no matter how many instances are created. (There don't need to be any instances created for that one slot to exist though.) The details of exactly which heap the variables live on are complicated, but explained in detail in an MSDN article on the subject.

    每个静态变量都存储在堆中,不管它是在引用类型中声明的,还是在值类型中声明的。无论创建多少实例,总共只有一个槽。(不过,不需要为这个插槽创建任何实例。)具体哪个堆的详细信息是复杂的,但是在MSDN的文章中详细解释了这个主题。

#8


0  

These are illustrations depicting above answer by @P Daddy

以上是@P Daddy给出的解答

数组、堆和堆栈和值类型。

数组、堆和堆栈和值类型。

And I illustrated the corresponding contents in my style.

我用我的风格说明了相应的内容。

数组、堆和堆栈和值类型。