4

同时在互联网上阅读我了解到静态变量总是具有相同的内存地址。因此,在编译程序时,编译器将决定分配给静态变量的内存地址。阅读让我想到当你这样做时会发生什么:

class Foo {}

class Program
{
    public static Foo someStaticVar;

    static void Main(string[] args)
    {
        Foo localVariable = new Foo();

        int x = 4;

        someStaticVar = localVariable; // is someStaticVariable still will have the same address?
    } 
    // variable x will be pushed of the stack
    // localVariable will be pushed of the stack
    // what happens then with someStatic var? 
}

我还了解到,在方法中声明变量时,它们将在创建时被推入堆栈,并在方法返回时弹出堆栈。如果这一切都是真的,那么 someStaticVar 应该消失,但它不会。

我确信我一定理解错了。或者,也许在线 someStaticVar = localVariable;正在执行该对象的深拷贝,但我对此表示怀疑,因为互联网上有很多关于如何对对象进行深拷贝的问题,它们与这种方法有很大不同。

4

4 回答 4

4

在您提供的示例中,localVariable它只是该Main方法的一个局部变量。一旦 Main 方法结束执行,它就会超出范围。但是由于您已将其分配给静态字段,因此创建的 Foo 实例将继续存在于 Main 方法之外。并且由于它是静态字段,它甚至会存在于 Program 类之外。

所以这就是一步一步发生的事情:

static void Main(string[] args)
{
    // an instance of Foo is created and pushed on the heap
    // localVariable is now pointing to the address of this instance
    // localVariable itself is stored on the stack
    Foo localVariable = new Foo();

    // someStaticVar is now pointing to the same location on the heap as
    // the localVariable - the Foo instance created earlier
    someStaticVar = localVariable; 
} 
// at this stage the Main method finishes executing and all local
// variables are falling out of scope. But since you have a static variable
// pointing to the Foo instance that was created inside the Main method this
// instance is not illegible for garbage collection because there are still 
// references to it (someStaticVar).

如果someStaticVar是一个实例字段并且不是静态的,那么将发生相同的过程,只是一旦不再有对包含类(程序)的任何引用,该实例将超出范围。

于 2012-09-18T14:19:12.413 回答
2

在方法中声明变量时,它们将在创建时被压入堆栈,并在方法返回时弹出堆栈。

Foo localVariable = new Foo();

将在堆上创建 Foo 对象,并且引用存储在堆栈上。当方法完成时,引用从堆栈中删除。垃圾收集器将完成从堆中删除 Foo 的工作,因为不会引用 Foo 对象。但,

someStaticVar = localVariable;

,将导致 someStaticVar 引用堆上的 Foo 对象。即使在方法退出后, someStaticVar 仍将引用 Foo 对象。所以,垃圾收集器不会收集那个 Foo 对象。 主要要记住的是,当创建引用类型的对象时,对象是在堆上创建的,而引用存储在堆栈上。

问题是“静态字段存储在哪里?”,对象的实例字段存储在堆上,局部变量存储在堆栈上,但是“静态变量存在于内存中的哪个位置?

于 2012-09-18T14:26:38.493 回答
1

没有对象的深拷贝:对象本身存储在堆上,而不是堆栈上。你是对的,someStaticVariable总是有相同的地址。我相信您的误解在于该地址存储的内容。

当您声明一个引用类型的变量(任何object类型,例如Foo在您的代码中)时,变量本身并不是对象:它是一个存储对象地址的变量。该地址只是一个数字。所以,“的地址someStaticVariable”不是Foo它本身的地址;它是地址的地址Foo

同样,这就是为什么你someStaticVar不会“消失”的原因。在您的代码中发生的是 aFoo在内存中的某个地址创建,并设置localVariable表示. 当您执行时,会发生地址是从复制到的,因此两个变量指向相同。消失时,不受影响。FoosomeStaticVar = localVariablelocalVariablesomeStaticVarFoolocalVariablesomeStaticVar

于 2012-09-18T14:23:31.667 回答
1

在这种情况下Foo是一个类,所以它是一个引用类型。类型变量Foo实际上只是指向内存中Foo实际存储对象的位置的指针。

当说静态变量具有恒定的内存地址时,并不是说该内存地址的值是恒定的。指向对象的指针的地址Foo不会改变,但存储在该固定内存位置的数字(在本例中为地址)很容易在各种潜在Foo对象(或null)之间变化。

除此之外,由于 C# 是一种托管语言而不是非托管语言,因此甚至不能说静态变量将具有恒定的内存地址。这当然是可能的,但它确实是您不应该依赖的 C# 内存管理器的实现细节。

于 2012-09-18T14:25:41.207 回答