與其在運行時定期運行垃圾檢測,我們爲什麼不讓編譯器在適當的地方自動插入free()?這樣,我們在編譯時只支付一次價格。爲什麼垃圾收集?爲什麼不編譯器自動插入free()呢?
編譯器知道變量超出範圍或被重新分配給不同對象的地方。因此,它可以找出對象是否不再可及,並在那裏自動插入free()。
不是嗎?爲什麼?
如果是因爲多線程,我們可以用單線程/綠色線程語言來完成嗎?
與其在運行時定期運行垃圾檢測,我們爲什麼不讓編譯器在適當的地方自動插入free()?這樣,我們在編譯時只支付一次價格。爲什麼垃圾收集?爲什麼不編譯器自動插入free()呢?
編譯器知道變量超出範圍或被重新分配給不同對象的地方。因此,它可以找出對象是否不再可及,並在那裏自動插入free()。
不是嗎?爲什麼?
如果是因爲多線程,我們可以用單線程/綠色線程語言來完成嗎?
編譯器知道變量超出範圍或被重新分配給不同對象的地方。
當然可以 - 對於變量。但是你不清楚變量 - 你清除它們指向的內存。只是因爲一個變量超出了範圍,這並不意味着指向的內存不再可用。
例如:
y = ...
{
x = new X();
if (todayIsTuesday()) {
y = x;
}
} // x just went out of scope
你不能讓一個編譯時決定存儲器是否指向x應在該段的最後一行被釋放,因爲它取決於的什麼日子當它是代碼是ran。
因此,要解決這個問題,這個決定必須委託給運行時,通過插入適當的邏輯,例如:
Y* y = ...
{
X* mem = new X();
X* x = mem;
markPointer(&x, mem);
if (todayIsTuesday()) {
y = x;
markPointer(&y, mem);
}
markNoLongerPointer(&x, mem);
} // x just went out of scope
隨着markNoLongerPointer()
清除給定爲第二參數的存儲器,如果它的內部維護的數據結構告訴它,x
是唯一參考那個內存......換句話說,這是引用計數邏輯的粗略起點。
編譯器當然可以將這種引用計數邏輯添加到編譯後的代碼中,有些可以,但正如其他人提到的那樣,引用計數有一些缺點:高開銷,週期問題,再加上它有時會導致顯着的暫停次,當對大數據結構的根的唯一引用超出範圍時。有辦法解決了這些缺點,雖然,但這是超出範圍的這個答案:-)
週期和超出範圍情況的根源是爲什麼例如Delphi不會對容器類型(結構體,類)進行refcounting。它仍然可以使用接口發生,但這些主要用於接口外部對象,而不是內部接口。對於普通類,它使用手動內存管理。 –
正如你可以在這個Wikipedia article on Reference Counting閱讀更多有關,也有引用了垃圾收集計數兩個主要的缺點:
就從根本上,在不平凡的資源管理情況下,程序員明確有free
內存(和其他資源)無論如何。它可能不完全類似於對free
的呼叫;它可能會從列表中刪除對象引用,以便垃圾收集器可以收集它,或將其設置爲空引用,或釋放智能指針,或從列表中刪除對象或對free
進行真正的調用。然而,程序員仍然必須以某種方式明確地指定它,以避免邏輯泄漏。
直到我們有編譯器能夠開始閱讀我們的想法,弄清楚我們正在製作什麼類型的軟件,或者直到我們開始用完全不同的方式編寫代碼時,才能在編譯期確定它。因爲想像一下像Photoshop這樣的圖像編輯器。何時應該釋放圖像?當用戶關閉它。這對我們人類來說是顯而易見的,但它不是信息編譯器所具有的。
數字音頻工作站的類似情況。音頻剪輯何時應該從內存中釋放出來?當用戶從剪輯編輯器中移除它時,例如這對於人類設計者來說也是顯而易見的,但對編譯器來說則不是這樣我的80年代音樂集何時應該從我的硬盤中刪除以節省空間?當我,用戶,明確刪除它們。直到軟件可以開始可靠地開始閱讀我的想法,它纔不會比手動/明確的更少。
因此,無論您是否有垃圾收集或RAII或其他任何其他用於這些非平凡情況的資源管理都沒有。在這種情況下,程序員必須始終明確地使用free
資源來響應正確的輸入/事件,而不管它是否是人爲的想法,並且設計非常特定於應用程序域,以確定何時應該釋放這些資源。
當然,對於一些微不足道的情況,如分配給函數給定範圍的內存,編譯器可以自動釋放它,而不需要任何垃圾回收,而且它們的確如此。即使在C:
void some_func(...)
{
int some_array[64];
...
// some_array's memory will be automatically freed from
// the stack when exiting the function.
}
...並且那裏C基本上做你的建議的類比等價物。它會產生增加/減少堆棧指針的指令,例如有效地分配和釋放內存,並使釋放的內存可供其他地方使用。使用C++,它甚至可以在使用RAII時爲堆分配的內存執行此操作,前提是擁有該內存的對象在退出函數的範圍時變得無法訪問。但是這些類型的臨時資源的生命週期與具有堆棧式分配/釋放推/拉模式的函數範圍相關聯,這些都是微不足道的情況。不平凡的情況總是要求程序員以某種方式明確指定何時不再需要資源,因爲不平凡的情況處理的持久性狀態的生存期不與任何給定函數的範圍相關聯。
你的意思是像C++ RAII? – Mysticial
你猜對了。它不能。事實上,一些程序員和計算機科學家實際上並不是無能的傻瓜(這裏是一個很大的驚喜)。現在爲了明白爲什麼,考慮最簡單的垃圾收集形式,引用計數。爲什麼需要運行時間計數?編譯器可以靜態計算計數嗎?需要怎樣的信息才能這樣做? –
@ n.m。我認爲在沒有指針算術語言的情況下是可能的。這個傻瓜肯定是在我身上,因爲我無法弄清楚爲什麼。 – Ron