前兩篇文章提出的優化方法,都是不需要修改源代碼的,而是在CLR或JIT層面進行自動優化的。但本文中提出的優化方法則需要引入新的語法,開發者只有在源代碼中使用了這些新語法,才會獲得優化。
1. 允許對象“嵌入式”組合:說白了,就是允許一個對象包含其他對象(包含的是對象本身,而非其引用),這樣就把多個對象合并成了一個對象,減少了對象的數量,自然GC的壓力就輕了。被包含的對象其實就相當于一個結構體(struct),禁止持有其引用。如果被包含對象是數組,至少應允許固定長度的情況,至于是否允許變長,則要看實現的難易程度了。
相關英文貼:
https://github.com/dotnet/roslyn/issues/2097
https://github.com/dotnet/roslyn/issues/6055
2. 把弱引用“升級”為語言特性,而不是作為一個類(為了兼容,原來的弱引用類也要保留)。目前的弱引用實現是CLR內部維護了長短兩個弱引用表來記錄弱引用,這樣做似乎沒有必要,而且引入了額外的開銷,令使用者有所顧慮。既然GC之后CLR能夠正確地更新本輪沒被回收的對象的(強)引用,那么按理說也能更新本輪已被回收的對象的弱引用,并不需要額外的弱引用表,至少短弱引用是這樣。如果能打消使用者的顧慮,使他們更近大膽地使用弱引用記錄緩存,使用得當的話也是能給減輕GC的壓力的。
相關英文貼:
https://github.com/dotnet/roslyn/issues/2171
3. 引入“引用計數”來輔助GC。可以讓CLR針對開發者指定的類來維護其實例的引用計數,有了引用計數,可以實現兩方面的優化。一是“寫時復制”,即帶有值類型特點的對象(類似string)可以被多處引用,只要不修改,就可以共享內存,而在修改時若引用計數為1則直接就地修改,若引用計數大于1,則先復制再修改復制的新對象。二是提前執行終結器,一旦某個對象的引用計數變為0,則立即執行其終結器函數(如果有的話),等到GC時即可直接回收其內存而無須再執行終結器,這就避免了終結器拖慢GC的積弊。當引用計數為0且無終結器時倒不是必須立即回收對象,因為這樣做將在下次GC之前造成內存碎片,降低分配新對象的速度,有得有失,可以作為一個可選特性由使用者決定是否立即回收。
相關英文貼:
https://github.com/dotnet/coreclr/issues/1792
結語:到此為止,我所能想到的內存優化方法已經全部寫完了,如果讀者有更好的辦法或者認為我的辦法有缺陷,歡迎在評論中指出。
新聞熱點
疑難解答