【问题标题】:What happens when you assign reference of local variable inside method to public static variable当您将方法内的局部变量的引用分配给公共静态变量时会发生什么
【发布时间】:2012-09-10 19:45:44
【问题描述】:

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

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; 正在执行该对象的深拷贝,但我对此表示怀疑,因为互联网上有很多关于如何对对象进行深拷贝的问题,它们与这种方法有很大不同。

【问题讨论】:

  • “内存地址”在托管语言中毫无意义。所以,不,编译器不会为 C# 中的任何变量预先分配内存地址。

标签: c# static heap-memory stack-memory


【解决方案1】:

在您提供的示例中,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 是一个实例字段并且不是静态的,那么将发生相同的过程,只是一旦不再有对包含类(程序)的任何引用,该实例将超出范围。

【讨论】:

  • 我认为建议Foo 的实例是在堆栈上创建的,这是一种误导——对象是在堆上创建的;对它的引用存储在堆栈中。
  • @DanPuzey,你是绝对正确的。感谢您指出了这一点。我已更新我的答案以考虑您的评论。
【解决方案2】:

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

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

同样,这就是您的someStaticVar 不会“消失”的原因。在您的代码中发生的情况是,Foo 在内存中的某个地址创建,localVariable 被设置为代表Foo内存中地址的值。当您执行someStaticVar = localVariable 时,会发生地址localVariable 复制到someStaticVar,因此两个变量都指向同一个Foo。当localVariable 消失时,someStaticVar 不受影响。

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

      Foo localVariable = new Foo();

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

      someStaticVar = localVariable;

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-19
        • 2017-09-15
        • 2022-01-21
        • 1970-01-01
        • 2016-01-10
        • 1970-01-01
        相关资源
        最近更新 更多