2010-03-15 26 views
15

我正在開發一款使用XNA和C#的遊戲,並試圖避免每幀調用new struct()類型代碼,因爲我認爲它會使GC出奇。 「但是等等,」我對自己說,「struct是一個值類型,那麼GC不應該被調用,對吧?」那麼,這就是我在這裏問的原因。創建值類型時會發生什麼?

我只對value類型會發生什麼非常模糊的想法。如果我在函數調用中創建一個新的結構體,那麼這個結構體是在堆棧上創建的嗎?它是否會被推動並彈出,性能不會受到影響?此外,如果我需要在一次調用中創建多個實例,是否會有一些內存限制或性能影響?

採取的,例如,此代碼:

spriteBatch.Draw(tex, new Rectangle(x, y, width, height), Color.White); 

矩形在這種情況下是一個結構。當新的Rectangle被創建時會發生什麼?不得不多次重複這條線的含義是什麼(比如說,成千上萬次)?是否創建了此矩形,將副本發送到Draw方法,然後丟棄(意味着沒有更多的內存被更多的Draw在同一個函數中以這種方式調用)?

P.S.我知道這可能是過時的優化,但我主要是好奇,希望對發生的事情有更好的理解。

回答

4

當一個新的結構被創建時,它的內容被直接放到你指定的位置 - 如果它是一個方法變量,它會進入堆棧;如果它被分配給一個類變量,它將進入被指向的類實例內部(在堆上)。

當一個結構變量被複制(或者,在你的情況下,傳遞給一個函數),構成結構的字節全部複製到堆棧上或類內的正確位置(如果你設置引用類型的實例上的字段或屬性)。

儘管可能會複製字節,但JIT編譯器可能會優化所有不必要的副本,以便儘可能快地執行。一般來說,這不是你需要擔心的事情 - 這是一個非常微觀的優化:)

這是否回答了您的問題?

+0

如果我反覆調用同一個函數,是否有新的Rectangle將每次調用都推送到Draw,並在Draw返回時彈出? – Bob 2010-03-15 18:31:00

+0

是的,但請記住推動和彈出堆棧幀只是遞增/遞減指針(單個機器指令)。無論如何,JIT編譯器會優化所有的王國。 – thecoop 2010-03-15 18:33:17

+0

@Bob:是的,儘管我猜想理論上可能的是,JIT *可以*看到相同的調用並緩存提交的值多次,但這似乎不太可能,因爲啓發式會涉及到檢測,並且很容易被排除。 – 2010-03-15 18:33:44

1

當值類型進入堆棧時,每個幀都會分配和釋放所有內存,特別是在Xbox 360上,仍然會影響性能。在PC上,您可能不會注意到它們之間的差異,但在360上可能會。

+0

並非所有值類型都在堆棧中。如果Customer類型的c的值類型爲int age,則其值仍然在堆上。 – JonH 2010-03-15 18:30:04

+0

真的嗎?在框架的開始處創建一個新的Rectangle並重用它會更好嗎? – Bob 2010-03-15 18:31:44

+0

最好的辦法是嘗試以兩種方式運行數百萬次並查看哪一個更快。您還可以比較兩種方法生成的IL。 – thecoop 2010-03-15 18:34:25

1

如果在本地聲明或在堆上對象實例的一部分(作爲對象實例的一部分)聲明,則會在堆棧上創建值類型。在任何情況下,GC都不收集結構實例,它們在容器超出範圍時被銷燬。

MSDN struct (C#)文章有關於此的更多信息。

1

這只是添加到thecoops的答案。對於引用類型,new運算符在堆上分配一個新類型的實例並調用指定的構造函數。

對於結構體,new運算符根據指定的構造函數初始化字段。但是,可以在不使用new的情況下實例化一個結構。在這種情況下,結構中的所有字段都是未初始化的,只有在明確初始化之後才能使用。請參閱the description on MSDN

相關問題