跟面試官聊.NET垃圾收集,直刺面試官G點


裝逼的面試官和裝逼的程序員

我面試別人的時候,經常是按這種路子來面試:

看簡歷和面試題,從簡歷和面試題上找到一些技術點,然后跟應聘者聊。

聊某個技術點的時候,應聘者的回答會牽涉到其他的技術點,然后我會一一記下來,再挑一些我感興趣的技術點繼續和他聊

有時候應聘者為了裝逼會牽涉出很多技術點,他自己可能只是知道個名字就說出來了。

這樣的話,能很輕易的發現應聘者的水平,也能知道他提供的面試信息的水分有多少。

---------------------

然而,有的時候會碰到一些我自己都不熟悉的技術點(比如說算法、圖形、WF方面的),

那我就盡量不跟人家聊這個,或者直接說,這個我也不太懂,

但是,有很多面試官跟應聘者聊天的時候,為了裝B,會天馬行空的聊出很多自己都不是很熟悉的技術點。

這樣來應聘的人就能很清楚的知道,將來自己是跟着一個什么樣的老大,在一個什么樣的團隊混了。

----------------------

大概在今年年初的時候,我面試過一個朋友,各方面都還不錯,

他主動提到垃圾收集這個技術點,於是我們就在這塊內容上聊起來

但是,我覺得他回答的內容,跟我想聽的內容,雖然有交集,但大部分還是扯遠了(我相信他對這塊還是比較清楚的)

就像有一個姑娘持續不斷的撩撥你,難受的不行,從那時開始就打算寫這篇文章了

(后來,我的面試建議是同意錄用,但最終沒有加入我們團隊,估計是人事或者薪資卡住了。) 

能簡單聊一下垃圾收集的工作方式嗎?

運行.NET應用程序時,程序創建出來的對象都會被CLR跟蹤,

哪些對象還會被用到(存在引用關系);哪些對象不會再被用到(不存在引用關系),CLR都是有記錄的。

CLR會整理不會再被用到的對象,在恰當的時機,按一定的規則銷毀一部分對象,釋放出這些對象所占用的內存。

-----------------

上面這段話,牽涉到了很多技術點:(以下這些技術點,簡單說一下,不深入討論)

CLR是怎么記錄對象引用關系的?

CLR會把對象關系做成一個“樹圖”,這樣標記他們的引用關系

CLR是怎么釋放對象的內存的?

關鍵的技術是:CLR把沒用的對象轉移到一起去,使內存連續,新分配的對象就在這塊連續的內存上創建,這樣做是為了減少內存碎片(CLR不會移動大對象)

垃圾收集器按什么規則收集垃圾對象?

CLR按對象在內存中的存活的時間長短,來收集對象。

時間最短的被分配到第0代,最長的被分配到第2代,一共就3代。

一般第0貸的對象都是較小的對象,第2代的對象都是較大的對象

第0代對象GC收集時間最短(毫秒級別),第2代的對象GC收集時間最長。

當程序需要內存時(或者程序空閑的時),GC會先收集第0代的對象,

收集完之后發現釋放的內存仍然不夠用,GC就會去收集第1代,第2代對象。(一般情況是按這個順序收集的)

如果GC跑過了,內存空間依然不夠用,那么就拋出了OutOfMemoryException異常。

GC跑過幾次之后,第0代的對象仍然存在,那么CLR會把這些對象移動到第1代,第1代的對象也是這樣。

既然有了垃圾收集器,為什么還要Dispose方法和析構函數?

因為CLR的緣故,GC只能釋放托管資源,不能釋放非托管資源(數據庫鏈接、文件流等)

那么該如何釋放非托管資源呢?

一般我們會選擇為類實現IDispose接口,寫一個Dispose方法。

讓調用者手動調用這個類的Dispose方法(或者用using語句塊來調用Dispose方法)

這是不錯的選擇,因為調用者最清楚該什么時候來釋放這些資源。

這個方法執行時,析構函數和垃圾收集器都還沒有開始處理這個對象的釋放工作

-------------------------

有時候,我們不想為一個類型實現Dispose方法,

我們想讓他自動的釋放非托管資源。那么就要用到析構函數了。

析構函數是個很奇怪的函數,調用者無法調用對象的析構函數,析構函數是由GC調用的。

你無法預測析構函數何時會被調用,所以盡量不要在這里操作可能被回收的托管資源,析構函數只用來釋放非托管資源

GC釋放包含析構函數的對象,比較麻煩(需要干兩次才能干掉她),

CLR會先讓析構函數執行,再收集它占用的內存。

我們需要手動執行垃圾收集嗎?什么場景下這么做?

GC何時執行垃圾收集是一個非常復雜的算法(策略)

大概可以描述成這樣:

如果GC發現上一次收集了很多對象,釋放了很大的內存,

那么它就會盡快執行第二次回收,

如果它頻繁的回收,但釋放的內存不多,

那么它就會減慢回收的頻率。

所以,盡量不要調用GC.Collect()這樣會破壞GC現有的執行策略。

除非你對你的應用程序內存使用情況非常了解,你知道何時會產生大量的垃圾,那么你可以手動干預垃圾收集器的工作 

我有一個大對象,我擔心GC要過很久才會收集他,

簡單聊一下弱引用和垃圾收集之間的關系?

假設有一個大對象,用完之后,引用關系沒有的時候(這一句更改過),這個時候GC隨時都有可能收集它,並釋放他占用的內存

但因為是一個較大的對象,很有可能在第3代,估計GC一時半會還不會去收集它。

這個對象已經在垃圾堆里了,但是我還想用它,怎么辦?怎么從垃圾堆里把它撈回來呢?

這個時候就用到了弱引用,來看看下面這段代碼:

            var bss = new BsCtl(BrowserContainer);
            var vbss = new WeakReference<BsCtl>(bss);
            bss = null;
            BsCtl ok;            
            vbss.TryGetTarget(out ok);
            //如果沒有進行垃圾收集OK不會為NULL
            if (ok == null)
            {
                //如果已經進行了垃圾收集,就會執行這段代碼
                ok = new BsCtl(BrowserContainer);
            }

垃圾收集隨時可以收集bss對象,

如果收集了,就會進入if語句塊,如果沒有收集,就不會進入if語句塊,TryGetTarget(out ok)就成功把bss從垃圾堆里撈回來了

注意:這里只說了短弱引用,沒有提及長弱引用,我覺得長弱引用使用的場景較少(謝謝園友imfunny的提醒)

垃圾收集器的好處

很多面試官都愛問這個問題,但我從來不問,

(其實我很少問關於垃圾收集方面的任何東西,除非應聘者自己談到這方面來)

因為我沒有很豐富的C/C++編程經驗,

如果想談垃圾收集器的好處,那么勢必要和C/C++這樣的較低級的語言對比。

應聘者大概可以說說,

減少內存使用不當的BUG,提升編程效率之類的問題

其他

這篇文章參考了CLR VIA C#第三版,另外還參考了博客園一位園友的博客,但地址已經找不到了 

 


免責聲明!

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



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