找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

掃一掃,訪問微社區

只需一步,快速開始

談談 .net 對象生命周期

查看: 270| 評論: 0| 發布者: 小勾

??? ??С
簡介:作者:攻城的獅來源: cnblogs.com/MaMaNongNong/p/11945161.html不用程序員操心的堆 — 托管堆程序在計算機上跑著,就難免會占用內存資源來存儲在程序運行過程中的數據,我們按照內存資源的存取方式將內存劃分為堆 ...

作者:攻城的獅

來源: cnblogs.com/MaMaNongNong/p/11945161.html

不用程序員操心的堆 — 托管堆

程序在計算機上跑著,就難免會占用內存資源來存儲在程序運行過程中的數據,我們按照內存資源的存取方式將內存劃分為堆內存棧內存。




棧內存,通常使用的場景是:對存取速度要求較高且數據量不大。




典型的棧內存使用的例子就是函數棧,每一個函數被調用時都會被分配一塊內存,這塊內存被稱為棧內存,以先進后出的方式存取數據。




在函數執行過程中不斷往函數棧中壓入(PUSH)數據(值類型數據:int、float、對象的引用...),函數執行完后又將函數棧中的數據逐個彈出(POP),由于是以操作棧的形式來存取,所以訪問速度快。

談談 .net 對象生命周期

堆內存,從字面意思上理解就好像是倉庫里面可以存一堆破爛,你若是需要存點什么東西就盡管往里面一扔,倉庫里有的是空間。




事實確實也是如此,堆內存中可以存放大規格的數據(比如對象資源),這些數據是不適合存放在棧中的,因為?臻g的容量有限,這就是堆內存相對于棧內存的好處:容量大。




但是它的缺點也是顯而易見的,那就是存取堆內存的數據相較于存取棧內存是非常慢的,試想一下,讓你在倉庫里的一堆破爛里去找你想要的東西是什么感覺。

棧內存比堆內存詳細參考:

blog.csdn.net/boyxiaolong/article/details/8543676



談談 .net 對象生命周期

從內存分配方式上看,堆內存不同于棧內存,函數棧是在每一個函數被執行的時候被自動分配并且函數執行完成后自動回收,而如果你想使用堆內存,就得自己動手豐衣足食。




所以你會看到c語言程序員會這樣去使用堆內存:

談談 .net 對象生命周期

當然,沒有接觸過c/c++的小伙伴也不用驚慌,上面只不過是想讓你知道在c/c++語言中,程序員要是想使用堆內存,那就必須顯式地編寫分配和釋放堆內存資源的代碼。




有人問:使用完堆內存資源后沒有手動釋放它會有什么后果嗎?




答案是:由于堆內存資源使用者未及時釋放內存會導致內存無法再次使用,從而造成內存資源的泄漏(浪費)。

就在這個時候,c#程序員笑了,只見他的手指非常輕盈優雅地在屏幕上敲出了下面這行代碼:
Carbmw=newCar();

一旁圍觀的c程序員和c++程序員驚呆了,c++程序員突然眼睛里閃著光,喊道:“你還沒有釋放堆內存的資源呢,你這樣是很危險的,會內存泄漏的,快,把釋放堆內存的代碼寫上!”




c#程序員似乎并不為所動,舒舒服服地靠在椅子上,用余光瞟了c++程序員一眼,說:“不用慌,不用慌,這個對象在托管堆上放的好好的呢,不用我操心”,于是,c#程序員便娓娓道來(呼呼大睡)...

在.NET的世界,使用new關鍵字創建一個對象,首先對象資源被分配在托管堆中,然后new會返回一個指向堆上對象的引用,而不是真正的對象本身。




如果在方法作用域中將引用變量聲明為本地變量,這個引用變量保存在棧內,以供應用程序以后使用。

 

談談 .net 對象生命周期

托管堆,顧名思義,就是托給別人管的堆,那么是誰在管理著這個堆上的對象資源呢?




答案是:CLR(Common Lanauage Runtime),對象的實例化結束以后,GC(垃圾回收器)將會在對象不再需要時將其銷毀。




也就是說,通過允許垃圾收集器負責銷毀對象,內存管理的麻煩就都交給CLR了,萬事大吉。

 

談談 .net 對象生命周期

看似問題好像都已水落石出,無非就是將堆內存資源回收交給了CLR去承擔。難道你就不想知道的更多一點?比如接著而來的問題:

1、垃圾回收器如何判斷一個對象什么時候不再需要?

2、垃圾回收器又在什么時候會執行垃圾清理的操作?




別急,帶著問題慢慢往下看。



CIL的new指令 — 垃圾回收的觸發者


c#中的new關鍵字最終會被編譯器翻譯成CIL的newobj指令,讓我們仔細查看一下CIL newobj指令的作用。

談談 .net 對象生命周期

首先,需要明白托管堆不僅僅是一個可由CLR訪問的隨機內存塊。




.NET垃圾回收器是堆的“清潔工”,出于優化的目的它會壓縮空閑的內存塊(當需要時)。




為了輔助壓縮,托管堆會維護一個指針(通常被叫做下一個對象指針或者是新對象指針),這個指針用來標識下一個對象在堆中分配的地址。




此外,newobj指令通知CLR來執行下列的核心任務:

(1) 計算要分配的對象所需的全部內存(包括這個類型的數據成員和類型的基類所需的內存)。




(2) 檢查托管堆來確保有足夠的空間來放置所申請的對象。如果有足夠的空間,會調用這個類型的構造函數,構造函數會返回一個指向內存中這個新對象的引用,這個新對象的地址剛好就是下一個對象指針上一次所指向的位置。




(3) 最后,在把引用返回給調用者之前,讓下一個對象指針指向托管堆中下一個可用的位置。




下面的圖解釋了在托管堆上分配對象的細節。

談談 .net 對象生命周期

在c#中分配對象是一個很頻繁的操作,照這樣下去托管堆上的空間遲早會被揮霍完,所以,重點來了,如果CLR 發現托管堆沒有足夠空間分配請求的類型時,它會執行一次垃圾回收來釋放內存。




當執行垃圾回收時,垃圾收集器臨時掛起當前進程中的所有的活動線程來保證在回收過程中應用程序不會訪問到堆。(一個線程是一個正在執行的程序中的執行路徑)。




一旦垃圾回收完成,掛起的線程又可以繼續執行了。還好,.NET 垃圾回收器是高度優化過的,所以用戶很少能察覺到應用程序中的短暫中斷。




通過對CIL的new指令作用的解讀,我們知道了:如果托管堆沒有足夠的空間分配一個請求的對象,則會執行一次垃圾回收。

(講到這里c#程序員停了下來,喝了口保溫杯里的枸杞紅棗大補茶,清了清嗓子,繼續開始解惑...)

應用程序根的作用 — 區分不可到達的對象

現在讓我們來討論一下垃圾回收器怎樣確定什么時候“不再需要”一個對象。為了理解細節,你需要知道應用程序根的概念。




簡單來說,一個根是一個引用,這個引用指向堆上面的一個對象的。嚴格來說,一個根可以有以下幾種情況:

(1) 指向全局對象的引用(盡管C#不支持,但CIL代碼允許分配全局對象)

(2) 指向任何靜態對象

(3) 指向一個應用程序代碼中的局部對象

(4) 指向傳入到一個函數中的對象參數

(5) 指向等待被終結(finalized)的對象

(6) 任何一個指向對象的CPU寄存器




在一次垃圾回收的過程中,運行環境會檢查托管堆上面的對象是否仍然是從應用程序根可到達的。




為了檢查可達,CLR會建立一個代表堆上每個可達對象的圖。對象圖用來記錄所有可達的對象。同時,注意垃圾回收器絕不會在圖上標記一個對象兩次,因此避免了煩人的循環引用。




假設托管堆上有名字為A,B,C,D,E,F和G的對象集合。在一次垃圾回收過程中,會檢查這些對象(同時包括這些對象可能包含的內部對象引用)是否是根可達的。




一旦圖被建立起來,不可達的對象(在此是對象C和F)被標記為垃圾。




下圖是上述場景的一個可能的對象圖(你可以把箭頭讀作依賴或者需要,例如"E依賴于G,間接依賴于B,“A不依賴任何對象”等)。

談談 .net 對象生命周期

創建的對象圖是用來決定哪些對象是應用程序根可達的




一旦一個對象已經被標記為終結(此例子中是C和F--在圖中沒有他倆),它在內存中就被清理掉了。




在此時,堆上的剩余內存空間被壓縮,這會導致CLR修改活動的應用程序根集合(和對應的指針)來指向正確的內存位置(這個操作是自動透明的)。




最后,調整下一個對象指針來指向下一個可用的內存位置。




下圖闡明了清除和壓縮堆的過程。

談談 .net 對象生命周期

到這里,通過對應用程序根的作用的理解,我們知道了如何知道一個對象是“不再需要”的。通俗點來說就是,這個對象在應用程序中已經無需被訪問了,成為了一座“孤島”,自然也就不再需要它了。

(為了讓c++程序員能更加理解. net垃圾回收的奧妙,c#程序員繼續滔滔不絕…)

理解對象的代 — 垃圾回收過程的優化

在嘗試找到不可達的對象時,CLR并不是檢查托管堆上的每個對象。很明顯,這樣做會消耗大量時間,尤其在大型(例如現實中)程序中。




為了幫助優化這個過程,堆上的每個對象被分配到一個特殊的"代”。




代這個概念背后的想法很簡單:對象在堆上存活的時間越長,接下來它繼續存在的可能性也就越大,即較舊的對象生存期長,較新的對象生存期短。




例如,實現Main()的對象一直在內存中,直到程序結束。相反,最近才被放到堆中的對象(例如在一個函數范圍里分配的對象)很可能很快就不可達。




在堆上的每個對象屬于以下的某一個代:
  • Generation 0:標識一個最近分配的還沒有被標記為回收的對象
  • Generation 1: 標識一個經歷了一次垃圾回收而存活下來的對象(例如,他被標記為回收,但由于堆空間夠用而沒有被清除掉)
  • Generation 2:標識一個經歷了不止一輪垃圾回收而存活下來的對象。


垃圾回收器首先會檢查generation 0的所有對象。如果標記并清理這些對象(譯者注:因為新對象的生存期往往較短,并且期望在執行回收時,應用程序不再使用第 0 級托管堆中的許多對象)后產生了足夠使用的內存空間,任何存活下來的對象就被提升到Generation 1。




為了理解一個對象的代如何影響回收的過程,可以查看下圖。下圖解釋了generation 0中一次垃圾回收后,存活的對象被提升的過程。

談談 .net 對象生命周期

generation 0 中的存活對象被提升到generation 1




如果所有的generation 0對象都被檢查了,但是產生的內存空間仍然不夠用,就檢查一遍generation 1中的所有對象的可達性并回收。存活下來的generation 1對象被提升到generation 2。




如果垃圾回收器仍然需要額外的內存,generation 2的對象就經歷檢查并被回收。此時,如果一個generation 2的對象存活下來,它仍然是一個generation 2的對象。




其實通過對象的代的設計是想達到這么一個效果:新對象(比如局部變量)會被很快回收,而老一些的對象(如一個應用程序對象)不會被經常騷擾。




說到底,對象代的設計就是為了優化垃圾回收的過程。

“我還有最后一個問題”,c++程序員按耐不住心里一直的疑惑,說到:“你說了這么多都是再講托管資源,難道.net中就沒有非托管資源嗎?. net又是怎么對非托管資源進行資源釋放的呢?”。




"這個問題問的好!",c#程序員大笑,于是接著又開始解惑(吹B)…

構建可終結對象 — 非托管資源處理第一式


以一名c#開發者的直覺告訴你,大多數的c#類都不需要顯式的清理邏輯。原因很簡單:如果類型使用了其他托管對象,一切都最終會被垃圾回收。




問:那在什么時候需要顯式地清理呢?




答案是:在你使用非托管資源時(例如原始的操作系統文件句柄、原始的非托管數據連接或其他非托管資源),才可能需要設計一個在用完后清理自身垃圾的類。




比如說下面這個類:

談談 .net 對象生命周期

現在問題來了,我們要在適當的時機調用數據庫連接類對象釋放資源的方法(SqlConnection類對象使用完后需要調用Dispose()方法釋放資源)。這個適當的時機當然就是對象在被CLR進行垃圾回收的過程中。




所以問題又來到了,有沒有一個方法是在這個時機被調用,而且是可以被擴展的呢?




是的,我們可以利用. NET的基類System.Object中定義的名為Finalize()的虛方法,也叫作終結器方法,它是這樣的:

 

談談 .net 對象生命周期

看到這當然會很奇怪,不是說有Finalize()方法,在哪,逗我?莫驚訝,其實這里的 ~Object() 就是Finalize(),只是一個語法糖罷了。

Finalize()的調用將(最終)發生在一次"自然的"垃圾回收或用程序通過GC.Collect()強制回收的過程中,所以這樣看來,終結器方法就是讓類對象釋放內部非托管資源的地方。




nice, 現在我們可以像這樣來編寫清理非托管資源的代碼:

談談 .net 對象生命周期

這樣被構建的對象被叫做可終結對象。




有關于終結過程的細節,在《C#與.NET4高級程序設計(第5版)》書中是這樣描述的:

談談 .net 對象生命周期

從以上的內容我們得知:通過Finalize()來清除非托管資源的時機只能是在.NET對象被垃圾回收的過程中,而且終結過程是一個消耗不小的動作。

問題又來了:很多非托管資源都非常寶貴(如數據庫和文件句柄),所以這些資源應該在使用完后盡快地被清除,而不能依靠垃圾回收的發生,那么這些資源應該以怎樣的形式被顯示地釋放呢?

談談 .net 對象生命周期

構建可處置對象 — 非托管資源處理第二式


除了重寫 Finalize() 之外,類還可以實現 IDisposable 接口,它定義了一個名為 Dispose() 的方法:
publicinterfaceIDisposable{  voidDispose();}

它的使用方法就是:在類的Dispose()方法中編寫非托管資源的釋放的代碼,程序員可以在這個對象不再需要的時候手動調用對象的Dispose()方法來達到及時釋放非托管資源的目的。




于是你可以像這樣來編寫類:



談談 .net 對象生命周期

采用這種方式來釋放非托管資源的類被稱作為可處置對象。




在這里還要補充一點,C#提供了一個語法糖來簡化調用Dispose()操作,如下:

談談 .net 對象生命周期

c++程序員說:“你這還不是要自己手動調用,如果我忘記調用 Dispose() 那豈不是一切都玩完!”




c#程序員冷笑一聲,“非也,非也,我來傳授你最后一招吧!”


非托管資源最強模式 — 雙劍合璧


人非圣賢,孰能無過。程序員也會有失手的時候,比如,忘記調用 Dispose() 方法...




這個時候就必須設計一個萬無一失的方法,達到一個目的:就是不管有沒有手動調用Dispose(),非托管資源最終都應該被妥妥地釋放掉。




為了解決這個問題,我們可以如下去定義一個可處置對象類:

談談 .net 對象生命周期

可以看到,這個類中即有終結方法的重寫也有Dispose()方法,這樣就能保證:程序員若忘記調用Dispose()方法釋放非托管資源,那么對象就會在垃圾回收的過程中調用終結方法來釋放非托管資源;




若程序員調用了Dispose()方法,那么 GC.SuppressFinalize(this) 會保證在垃圾回收過程中不再會調用對象的終結方法,避免不必要的資源開銷?芍^“雙劍合璧”,保萬無一失。

話音剛落,c++程序員雙手死死拉住c#程序員的褲子,"師父,收我為徒吧!我也要學c#…",c#程序員不想自己的褲子被扯破,于是答應了他。掏出一本上古神書…

談談 .net 對象生命周期

哦,不,拿錯了。。。應該是這本。。。

談談 .net 對象生命周期




【免責聲明】本文僅代表作者或發布者個人觀點,不代表SEO研究協會網(www.heffgx.live)及其所屬公司官方發聲,對文章觀點有疑義請先聯系作者或發布者本人修改,若內容涉及侵權或違法信息,請先聯系發布者或作者刪除,若需我們協助請聯系平臺管理員,郵箱[email protected](本平臺不支持其他投訴反饋渠道,謝謝合作)。若需要學習以上相關知識請到巨推學院觀看視頻教程,網址www.jutuiedu.com。

雞蛋

鮮花

握手

雷人

路過
已有 0 人參與

會員評論

推薦閱讀

    2020-04-05 13:16
  • 作者:yang0719

    個人如何運營自媒體?

    現在是一個互聯網時代,可以說只要有網絡就能在網上發聲,既然是是互聯網時代那么必然會有人利用其獲取收益。目前的自媒體運營就是借助網絡的力量進行收益獲取,而且現在自媒體的門檻也不是很高,很多人都開始運營起

  • 2020-04-05 13:02
  • 作者:stefanielin1130

    國家糧食和物資儲備局:沒必要囤米搶面

    國家糧食和物資儲備局:個別國家限制出口,對國內面粉、大米等口糧供應基本沒有影響。過度恐慌、搶購糧油,大可不必。(央視財經記者 鄒曉敏 王驍馳)【免責聲明】本文僅代表作者或發布者個人觀點,不代表SEO研究協會

  • 2020-04-05 12:55
  • 作者:半夏微涼

    魯南制藥集團網絡培訓練“內功”

    □記者 紀偉 通訊員 杜永武 報道本報臨沂訊 4月1日,記者從魯南制藥集團獲悉,面對疫情防控期間企業難以開展聚集式培訓的難題,魯南制藥集團主動出擊,開好分類培訓“藥方”,全方位滿足企業戰略發展和轉型創新下的

  • 2020-04-05 11:14
  • 作者:mileseo

    央行副行長劉國強:絕不會讓市場出現錢荒

    視頻加載中...國務院聯防聯控機制今日上午舉行新聞發布會,中國人民銀行副行長劉國強表示,我們絕不會讓市場出現錢荒,當然,錢也不要溢出來。滿足市場需求合理充裕。(央視財經記者 鄒曉敏 王驍馳) 【免責聲明】本

  • 2020-04-05 10:39
  • 作者:陳倉河谷

    范縣公務員網絡培訓實現全覆蓋

    新冠肺炎疫情發生以來,線下集中培訓按下“暫停鍵”,范縣依托河南干部網絡培訓學院學習平臺積極開展網絡培訓,全縣公務員和參照公務員法管理人員逐人建立學習賬號,實現應學盡學、應訓盡訓,全覆蓋,無盲區。目前,

  • 2020-04-05 08:44
  • 作者:ridxgu

    財政部:預計約提前2.5個月完成既定專項債發行任務

    央視財經(記者 吳南馨)今天上午,國務院聯防聯控機制舉行新聞發布會,介紹了增加地方政府專項債規模和強化對中小微企業普惠性金融支持的有關情況。財政部副部長許宏才表示,增加地方政府專項債券提前下達限額,有

  • 2020-04-05 08:12
  • 作者:LuLu_88

    公安部:開展專項行動嚴厲打擊影視、網絡培訓領域侵權盜版犯罪

    4月2日,南都記者從公安部獲悉,近日,公安部推出公安機關依法打擊食藥環和知識產權領域犯罪保障疫情防控期間復工復產十項措施,依法嚴厲打擊制假售假等違法犯罪活動,全力保障復工復產,促進經濟恢復發展。十條措施

  • 2020-04-05 07:03
  • 作者:mtfukankan

    央行副行長劉國強:動用存款基準利率需要進行更加充分的評估

    央視財經(記者 吳南馨)今天上午,國務院聯防聯控機制舉行新聞發布會,介紹了增加地方政府專項債規模和強化對中小微企業普惠性金融支持的有關情況。視頻加載中...中國人民銀行副行長劉國強表示,貸款利率下行表面上

  • 2020-04-05 06:18
  • 作者:龔呆子

    北京冬奧組委滑雪戰隊加強網絡培訓

    北京日報本報訊(記者 吳東)3月28日晚,北京冬奧組委滑雪戰隊舉辦了新一期網絡集中教學。這是新冠肺炎疫情發生以來,300多名滑雪戰隊隊員參與的第六次冬奧在線學習課。此次學習課由滑雪戰隊特邀隊員、圓滿完成南極遠

  • 2020-04-05 04:58
  • 作者:franky_chan

    港股神州租車停牌 其大股東陸正耀亦為瑞幸咖啡大股東

    港股神州租車停牌,此前一度跌超70%。昨日瑞幸咖啡自曝偽造交易22億元,最終收跌75%,神州租車及瑞幸咖啡的大股東均為陸正耀。(央視財經記者 鄒曉敏 王驍馳)【免責聲明】本文僅代表作者或發布者個人觀點,不代表SEO

文章排行

TOP ARTICLES

返回頂部
幸运飞艇公式规律