來自Microsoft研究院、劍橋大學和普林斯頓大學的一些研究人員構建了一個.NET的分支,實現了在運行時中添加支持手工內存管理的API。研究方法的細節及所獲得的性能提升發表在名為“Project Snowflake: Non-blocking Safe Manual Memory Management in .NET”(“雪花”項目:非阻塞的、安全的.NET手工內存管理)的論文中。
“雪花“項目意在實現.NET中的手工內存管理,這一改進被認為對一些應用是非常有用。C#、.NET等現代編程語言都采用了垃圾回收機制,使編程人員得以從管理對象的任務中解放出來,這一機制的優點廣為人知,涉及提高生產力、改進程序穩定性、內存安全及防止惡意操作等方面。但是垃圾回收機制需要付出一些性能上的代價,盡管這在很多情況下不易被察覺,但是在一些情況下還是存在問題的。“雪花”項目的研究人員就指出,對于在具有上百GB堆內存的系統上運行數據分析和流處理任務,就可受益于手工內存管理。
“雪花”項目所引入的手工內存管理是與垃圾回收機制并行工作的,開發人員一般情況下使用的是垃圾回收機制,但在環境需要時也可以選擇手工內存管理。該引入到運行時中的改進并不會對已有應用產生影響,并且會改進多線程應用的性能。“雪花”項目實現了“在程序任一位置分配和釋放獨立對象,并確保手工管理對象同樣享有完全的類型安全和時序安全,即使存在并發訪問時。”
“雪花”中提出了兩個新概念,即對象“所有者”(Owner)和“護盾”(Shield),它們實現為CoreCLR和CoreFX層級的API。“所有者”表示了棧或堆中的一個位置,保存了對手工堆中分配對象的唯一引用。“所有者”獲取自“護盾”,而引入“護盾”是為了避免手工對象在被多個線程訪問時重分配(deallocate)。“護盾”確保了當最后使用一個對象的線程重分配該對象后,才從堆中移除該對象。論文中是如下詳細闡述該機制的:
我們的解決方案……受到了無鎖數據結構研究中的“風險指針”(Hazard Pointer)這一概念的啟發。我們引入了一種機制,當線程想要通過其中一個“所有者”位置訪問手工對象時,這一意圖將會發布在線程本地狀態(TLS,Thread-Local State)中。此注冊過程可看成是創建了一個“護盾”,該“護盾”將保護對象不會被重分配,并授權發布注冊的線程可直接訪問對象,例如調用對象的方法,或是轉換(mutate)對象的字段。同時,不允許任何線程(同一線程或另一個線程)重分配對象及回收(reclaim)對象的內存。一旦客戶代碼不再需要訪問該對象,就可以釋放(dispose)“護盾”,即從對象的TLS中移除了指向該對象的引用。直接訪問從“護盾”獲取的對象是不安全的操作,因為在釋放“護盾”后,實際的重分配操作依然允許繼續。
論文中提供了一系列給定場景下的測試結果,表明使用“雪花”項目的性能相比于垃圾回收機制取得了改進。其中,“在峰值工作集上獲得了高達三倍的性能提高,在運行時上取得了兩倍的性能提高”。測試結果給出了很好的性能改進。這是因為當對象池非常大時,垃圾回收為釋放內存需要花費很多時間遍歷對象圖。
Microsoft并未詳述是否有規劃在.NET中加入“雪花”項目。但考慮到這是一種非侵入式的和安全的機制,我們希望在.NET的未來版本中能集成類似的功能。
查看英文原文: Microsoft Explores Manual Memory Management in .NET with Snowflake