變量內存分配知多少


  繁忙的工作總容易讓我們忽視最基礎的知識,手里的活停一停,下樓呼吸下新鮮空氣(北京的朋友抱歉了),讓大腦切換下進程。

  回想工作中我們所遇到的難點,嗯,好多都是我們對基礎知識了解得不夠透徹,或者說只知道了表層的東西。而往往我們總是被這些表層東西所欺騙了,最后等待我們的就是bug量增多,性能低下,運行不穩定,維護成本劇增,客戶滿意度下降,更嚴重的便可能導致項目進入惡性循環。可想而知,我們所面臨的挑戰是多么艱巨。

  言歸正傳,本節我們主要講的內容是.Net中變量內存分配,是的,就講這么簡單的知識。

一、我們先看下類的成員變量:

public class Customer

{

        int customerId;

        string customerName;

}

 

public class Customer

{

        int customerId = 0;

        string customerName = null;

}

  乍看之下,我們會覺得第二種寫法更加合情合理,但我們的編譯器沒有那么智能。他總會首先幫我們將成員變量賦予一個初始值,當我們人為賦予初始值的時候,編譯器會在調用默認構造函數之前用我們賦予的初始化值替換他默認初始化的值。也就是除非我們初始化的數據是業務相關數據,否則我們賦默認值則給JIT增加了負擔。

二、靜態變量:

  當我們增加個CustomerCount int類型靜態變量,並賦予初始值0時,這個時候我們會發現,編譯器默認給我們創建了一個靜態構造函數,並在靜態構造函數中替換掉編譯器默認初始化的值。如果我們不人為初始化呢,其實編譯器還會自動為我們提供一個靜態的構造函數,除非是我們的類沒有靜態變量。靜態構造函數只會執行一次

三、常量:

  1、我們知道靜態常量無需分配內存的,而且必須定義的時候就得賦初始化值,這樣的優點我們也不難發現他會帶來潛伏的bug,當他的引用(其實是那個常量值)分布到不同的程序集中時,如果后期由於業務變化需要改變常量值時,項目必須整體重新編譯一次,否則其他引用的程序集常量還保持原來值。可以看到,這給我們維護帶來了不便。

  2、由於靜態常量帶來的不便,所以這個時候動態常量便應運而生了,動態常量需要分配內存的,其必須在聲明或在構造函數中賦初始化值,如果不人為賦值,編譯器會初始化默認值的,但這樣的常量是毫無意義的。

四、局部變量:

  我們都很清楚,C#中訪問局部變量的之前,我們必須人為為他賦初值,如果沒有賦值,則編譯就不會通過。其實這個時候,編譯器是很聰明的,我們聲明了一個局部變量,但不給他賦值,我們要這個局部變量干嘛呢(閉包情況不在這個范圍)?他並不是類成員變量,可以共享。反過來想一想,如果編譯器也為局部變量賦初值,那么JIT的負擔得多么重。所以默認情況下,C#是不會初始化局部變量的。

下面我們來看看局部變量內存分配的情況:

public class Customer

{

        public Customer GetCustomer()

        {

            int customerId = 4;

            Customer customer = GetCustomerById(customerId);

            return customer;

        }

 

        private Customer GetCustomerById(int id)

        {

            Customer customer = new Customer();

            return customer;

        }

}

好,具體情況我們來看圖:

說明:①②給局部變量customerId賦值。

③④將this和customerId值壓入堆棧,准備調用GetCustomerById方法。

說明:⑤⑥調用GetCustomerById方法

⑦⑧在托管堆上創建Customer對象,並給局部變量customer賦值

說明:⑨⑩給返回值returnObj賦值,returnObj是編譯器創建的臨時存放返回值得局部變量

⑪將返回值的內容壓入堆棧

說明:⑫GetCustomerById方法執行完畢,將返回值傳遞到調用方維護的堆棧里。此時GetCustomerById局部變量都被堆棧彈出,這部分內存自動收回。

⑬給局部變量customer賦值

說明:⑭⑮給局部變量returnObj賦值

⑯將返回值壓入堆棧中

  上圖,只是為了形象說明局部變量內存分配情況,並不是最終本地代碼執行時內存情況,JIT還會為我們做很多優化,而且局部變量表沒有體現到堆棧中。

 

  從上圖中,我們可以知道,局部變量中值類型的值存放到堆棧中(並不是所有值類型都存放在堆棧區,有例外情況,比如閉包和yield的情況就比較特殊,編譯器會提升局部變量的),而引用類型則是在托管堆中開辟內存存放引用類型對象,其對象引用則是存放到堆棧中。

    ——Aaron.Pan


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM