Что происходит, когда вы назначаете ссылку на локальную переменную внутри метода общедоступной статической переменной

Между тем, читая в Интернете, я узнал, что статические переменные всегда имеют один и тот же адрес памяти. Поэтому при компиляции программы компилятор решает, какой адрес памяти назначить статическим переменным. Чтение заставило меня задуматься о том, что происходит, когда вы это делаете:

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; выполняется глубокая копия этого объекта, но я сомневаюсь, потому что в интеренете много вопросов о том, как сделать глубокую копию объекта, и они сильно отличаются от этого подхода.


person Tono Nam    schedule 18.09.2012    source источник
comment
адрес памяти не имеет смысла в управляемом языке. Так что нет, компилятор не присваивает заранее адреса памяти какой-либо переменной в C#.   -  person Peter Ritchie    schedule 18.09.2012


Ответы (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 было полем экземпляра, а не статическим, то произойдет тот же процесс, за исключением того, что экземпляр выйдет из области видимости, как только больше не будет ссылок на содержащий класс (программу).

person Darin Dimitrov    schedule 18.09.2012
comment
Я думаю, что неверно предполагать, что экземпляр Foo создается в стеке — объект создается в куче; ссылка на него хранится в стеке. - person Dan Puzey; 18.09.2012
comment
@DanPuzey, ты абсолютно прав. Спасибо за указание на это. Я обновил свой ответ, чтобы учесть ваше замечание. - person Darin Dimitrov; 18.09.2012

при объявлении переменных внутри метода они будут помещаться в стек при создании и извлекаться из стека при возврате из метода.

Foo localVariable = новый Foo();

создаст объект Foo в куче, а ссылка будет храниться в стеке. когда метод завершается, ссылки удаляются из стека. Сборщик мусора выполнит работу по удалению Foo из кучи, так как не будет ссылки на объект Foo. Но,

некотораяСтатическаяПеременная = локальнаяПеременная;

, приведет к тому, что someStaticVar будет ссылаться на объект Foo в куче. Даже после выхода из метода someStaticVar по-прежнему будет ссылаться на объект Foo. поэтому сборщик мусора не будет собирать этот объект Foo. Главное, что нужно помнить, это то, что при создании объекта ссылочного типа объект создается в куче, а ссылка сохраняется в стеке.

Вопросы "где хранится статическое поле?", поля экземпляра объекта хранятся в куче, локальные переменные хранятся в стеках, но где "статическая переменная существует в памяти?""

person amit gaikwad    schedule 18.09.2012

Глубокого копирования объекта нет: сам объект хранится в куче, а не в стеке. Вы правы, что someStaticVariable всегда будет иметь один и тот же адрес. Ваше непонимание, я думаю, связано с тем, что хранится по этому адресу.

Когда вы объявляете переменную ссылочного типа (любого типа object, например Foo в вашем коде), сама переменная не является объектом: это переменная, в которой хранится адрес вашего объекта. Этот адрес просто номер. Таким образом, «адрес someStaticVariable» не является адресом самого Foo; это адрес адреса Foo.

Точно так же, вот почему ваш someStaticVar не «исчезает». Что происходит в вашем коде, так это то, что Foo создается по некоторому адресу в памяти, а localVariable устанавливается на значение, которое представляет адрес в памяти Foo. Когда вы выполняете someStaticVar = localVariable, происходит следующее: адрес копируется из localVariable в someStaticVar, и, таким образом, обе переменные указывают на один и тот же Foo. Когда localVariable исчезает, someStaticVar не затрагивается.

person Dan Puzey    schedule 18.09.2012

В данном случае Foo — это класс, поэтому это ссылочный тип. Переменная типа Foo на самом деле является просто указателем на место в памяти, где на самом деле хранится объект Foo.

Когда говорят, что статическая переменная имеет постоянный адрес памяти, это не говорит о том, что значение этого адреса памяти является постоянным. Адрес указателя на объект Foo не изменится, но число (в данном случае адрес), хранящееся в этой фиксированной ячейке памяти, может легко варьироваться между различными потенциальными объектами Foo (или null).

Кроме того, поскольку C# является управляемым языком, а не неуправляемым языком, неверно даже утверждать, что статическая переменная будет иметь постоянный адрес памяти. Это, конечно, возможно, но на самом деле это деталь реализации диспетчера памяти C#, на которую вам не следует полагаться.

person Servy    schedule 18.09.2012