中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

.net中對象的生命周期有哪些-創(chuàng)新互聯(lián)

.net中對象的生命周期有哪些?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

成都創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設,堯都企業(yè)網(wǎng)站建設,堯都品牌網(wǎng)站建設,網(wǎng)站定制,堯都網(wǎng)站建設報價,網(wǎng)絡營銷,網(wǎng)絡優(yōu)化,堯都網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學習、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

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

棧內(nèi)存,通常使用的場景是:對存取速度要求較高且數(shù)據(jù)量不大。

典型的棧內(nèi)存使用的例子就是函數(shù)棧,每一個函數(shù)被調(diào)用時都會被分配一塊內(nèi)存,這塊內(nèi)存被稱為棧內(nèi)存,以先進后出的方式存取數(shù)據(jù),在函數(shù)執(zhí)行過程中不斷往函數(shù)棧中壓入(PUSH)數(shù)據(jù)(值類型數(shù)據(jù):int、float、對象的引用...),函數(shù)執(zhí)行完后又將函數(shù)棧中的數(shù)據(jù)逐個彈出(POP),由于是以操作棧的形式來存取,所以訪問速度快。

.net中對象的生命周期有哪些

堆內(nèi)存,從字面意思上理解就好像是倉庫里面可以存一堆破爛,你若是需要存點什么東西就盡管往里面一扔,倉庫里有的是空間。事實確實也是如此,堆內(nèi)存中可以存放大規(guī)格的數(shù)據(jù)(比如對象資源),這些數(shù)據(jù)是不適合存放在棧中的,因為??臻g的容量有限,這就是堆內(nèi)存相對于棧內(nèi)存的好處:容量大。但是它的缺點也是顯而易見的,那就是存取堆內(nèi)存的數(shù)據(jù)相較于存取棧內(nèi)存是非常慢的,試想一下,讓你在倉庫里的一堆破爛里去找你想要的東西是什么感覺。

.net中對象的生命周期有哪些

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

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

int *p = (int*)malloc(sizeof(int)); //在堆內(nèi)存中申請一塊字節(jié)數(shù)為int字節(jié)數(shù)的堆內(nèi)存,并返回指向該內(nèi)存區(qū)域的指針
*p = 10; 
free(p); //釋放堆內(nèi)存資源

你還會看見c++程序員這樣寫:

Car* bmw = new Car(); //創(chuàng)建一個Car類對象,在堆內(nèi)存中存放對象數(shù)據(jù),并返回指向?qū)ο筚Y源的指針
delete bmw; //釋放堆內(nèi)存資源

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

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

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

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

Car bmw = new Car();

一旁圍觀的c程序員和c++程序員驚呆了,他們不知道自己在敲代碼的時候有沒有像這樣輕松過。c++程序員用手撫摸著他那锃光瓦亮的額頭,突然眼睛里閃著光,喊道:“你還沒有釋放堆內(nèi)存的資源呢,你這樣是很危險的,會內(nèi)存泄漏的,快,把釋放堆內(nèi)存的代碼寫上!”

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

在.NET的世界,使用new關鍵字創(chuàng)建一個對象,首先對象資源被分配在托管堆中,然后new會返回一個指向堆上對象的引用,而不是真正的對象本身。如果在方法作用域中將引用變量聲明為本地變量,這個引用變量保存在棧內(nèi),以供應用程序以后使用。

.net中對象的生命周期有哪些

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

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

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

.net中對象的生命周期有哪些

.net中對象的生命周期有哪些

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

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


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


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

CIL的new指令— 垃圾回收的觸發(fā)者

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

.net中對象的生命周期有哪些

首先,需要明白托管堆不僅僅是一個可由CLR訪問的隨機內(nèi)存塊。.NET垃圾回收器是堆的“清潔工”,出于優(yōu)化的目的它會壓縮空閑的內(nèi)存塊(當需要時)。為了輔助壓縮,托管堆會維護一個指針(通常被叫做下一個對象指針或者是新對象指針),這個指針用來標識下一個對象在堆中分配的地址。

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

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

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

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

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

.net中對象的生命周期有哪些

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

當執(zhí)行垃圾回收時,垃圾收集器臨時掛起當前進程中的所有的活動線程來保證在回收過程中應用程序不會訪問到堆。(一個線程是一個正在執(zhí)行的程序中的執(zhí)行路徑)。一旦垃圾回收完成,掛起的線程又可以繼續(xù)執(zhí)行了。還好,.NET 垃圾回收器是高度優(yōu)化過的,所以用戶很少能察覺到應用程序中的短暫中斷。

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

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

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

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

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

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

(2)指向任何靜態(tài)對象

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

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

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

(6)任何一個指向?qū)ο蟮腃PU寄存器

在一次垃圾回收的過程中,運行環(huán)境會檢查托管堆上面的對象是否仍然是從應用程序根可到達的。為了檢查可達,CLR會建立一個代表堆上每個可達對象的圖。對象圖用來記錄所有可達的對象。同時,注意垃圾回收器絕不會在圖上標記一個對象兩次,因此避免了煩人的循環(huán)引用。

假設托管堆上有名字為A,B,C,D,E,F(xiàn)和G的對象集合。在一次垃圾回收過程中,會檢查這些對象(同時包括這些對象可能包含的內(nèi)部對象引用)是否是根可達的。一旦圖被建立起來,不可達的對象(在此是對象C和F)被標記為垃圾。

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

.net中對象的生命周期有哪些

(創(chuàng)建的對象圖是用來決定哪些對象是應用程序根可達的。)

一旦一個對象已經(jīng)被標記為終結(jié)(此例子中是C和F--在圖中沒有他倆),它在內(nèi)存中就被清理掉了。在此時,堆上的剩余內(nèi)存空間被壓縮,這會導致CLR修改活動的應用程序根集合(和對應的指針)來指向正確的內(nèi)存位置(這個操作是自動透明的)。最后,調(diào)整下一個對象指針來指向下一個可用的內(nèi)存位置。

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

.net中對象的生命周期有哪些

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

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

理解對象的代 — 垃圾回收過程的優(yōu)化

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

為了幫助優(yōu)化這個過程,堆上的每個對象被分配到一個特殊的"代”。代這個概念背后的想法很簡單:對象在堆上存活的時間越長,接下來它繼續(xù)存在的可能性也就越大,即較舊的對象生存期長,較新的對象生存期短。例如,實現(xiàn)Main()的對象一直在內(nèi)存中,直到程序結(jié)束。相反,最近才被放到堆中的對象(例如在一個函數(shù)范圍里分配的對象)很可能很快就不可達。

在堆上的每個對象屬于以下的某一個代:

Generation 0: 標識一個最近分配的還沒有被標記為回收的對象

Generation 1: 標識一個經(jīng)歷了一次垃圾回收而存活下來的對象(例如,他被標記為回收,但由于堆空間夠用而沒有被清除掉)

Generation 2:標識一個經(jīng)歷了不止一輪垃圾回收而存活下來的對象。

垃圾回收器首先會檢查generation 0的所有對象。如果標記并清理這些對象(譯者注:因為新對象的生存期往往較短,并且期望在執(zhí)行回收時,應用程序不再使用第 0 級托管堆中的許多對象)后產(chǎn)生了足夠使用的內(nèi)存空間,任何存活下來的對象就被提升到Generation 1。為了理解一個對象的代如何影響回收的過程,可以查看下圖。下圖解釋了generation 0中一次垃圾回收后,存活的對象被提升的過程?! ?/p>

.net中對象的生命周期有哪些

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

如果所有的generation 0對象都被檢查了,但是產(chǎn)生的內(nèi)存空間仍然不夠用,就檢查一遍generation 1中的所有對象的可達性并回收。存活下來的generation 1對象被提升到generation 2。如果垃圾回收器仍然需要額外的內(nèi)存,generation 2的對象就經(jīng)歷檢查并被回收。此時,如果一個generation 2的對象存活下來,它仍然是一個generation 2的對象。

其實通過對象的代的設計是想達到這么一個效果:新對象(比如局部變量)會被很快回收,而老一些的對象(如一個應用程序?qū)ο螅┎粫唤?jīng)常騷擾。

說到底,對象代的設計就是為了優(yōu)化垃圾回收的過程?! ?/p>

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

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

構(gòu)建可終結(jié)對象 —非托管資源處理第一式

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

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

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

比如說下面這個類:

//數(shù)據(jù)庫上下文類
  public class SqlDbContext
  {
    //...(其他被引用的對象實例)

    //類中包含的非托管資源(需要調(diào)用 Dispose()函數(shù)進行資源的釋放)
    SqlConnection sqlConnection = new SqlConnection("..."); 
      
  }

現(xiàn)在問題來了,我們要在適當?shù)臅r機調(diào)用數(shù)據(jù)庫連接類對象釋放資源的方法(SqlConnection類對象使用完后需要調(diào)用Dispose()方法釋放資源)。這個適當?shù)臅r機當然就是對象在被CLR進行垃圾回收的過程中,所以問題又來到了,有沒有一個方法是在這個時機被調(diào)用,而且是可以被擴展的呢?

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

.net中對象的生命周期有哪些

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

Finalize()的調(diào)用將(最終)發(fā)生在一次"自然的"垃圾回收或用程序通過GC.Collect()強制回收的過程中,所以這樣看來,終結(jié)器方法就是讓類對象釋放內(nèi)部非托管資源的地方。nice,現(xiàn)在我們可以像這樣來編寫清理非托管資源的代碼:

//數(shù)據(jù)庫上下文類
    public class SqlDbContext
    {
      //...(其他被引用的對象實例)

      //類中包含的非托管資源(需要調(diào)用 Dispose()函數(shù)進行資源的釋放)
      SqlConnection sqlConnection = new SqlConnection("..."); 

      ~SqlDbContext()
      {
        //這里清除非托管資源
        this.sqlConnection.Dispose();
      }
      
    }

這樣被構(gòu)建的對象被叫做可終結(jié)對象。

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

.net中對象的生命周期有哪些

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

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

.net中對象的生命周期有哪些

構(gòu)建可處置對象—非托管資源處理第二式

除了重寫 Finalize() 之外,類還可以實現(xiàn) IDisposable 接口,它定義了一個名為 Dispose() 的方法:

 public interface IDisposable
  {
    void Dispose();
  }

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

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

//數(shù)據(jù)庫上下文類
    public class SqlDbContext:IDisposable
    {
      //...(其他被引用的對象實例)

      //類中包含的非托管資源(需要調(diào)用 Dispose()函數(shù)進行資源的釋放)
      SqlConnection sqlConnection = new SqlConnection("..."); 

      public void Dispose()
      {
        //這里清除非托管資源
        this.sqlConnection.Dispose();
      }
    }

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

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

SqlDbContext context = new SqlDbContext();

  try
  {
    //在此作用域內(nèi)使用SqlDbContext類對象context
  }
  finally
  {
    //確保使用完后調(diào)用Dispose()方法
    context.Dispose();
  }

上面這段代碼等同于下面這段代碼:

using (SqlDbContext context = new SqlDbContext())
  {
    //在此作用域內(nèi)使用SqlDbContext類對象context
  }

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

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

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

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

這個時候就必須設計一個萬無一失的方法,達到一個目的:就是不管有沒有手動調(diào)用Dispose(),非托管資源最終都應該被妥妥地釋放掉。為了解決這個問題,我們可以如下去定義一個可處置對象類:

//數(shù)據(jù)庫上下文類
    public class SqlDbContext:IDisposable
    {
      //...(其他被引用的對象實例)

      //類中包含的非托管資源(需要調(diào)用 Dispose()函數(shù)進行資源的釋放)
      SqlConnection sqlConnection = new SqlConnection("..."); 

      ~SqlDbContext()
      {
        //這里清除非托管資源
        this.sqlConnection.Dispose();
      }

      public void Dispose()
      {
        //這里清除非托管資源
        this.sqlConnection.Dispose();

        //跳過終結(jié)過程
        GC.SuppressFinalize(this);
      }

關于.net中對象的生命周期有哪些問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關知識。

網(wǎng)站欄目:.net中對象的生命周期有哪些-創(chuàng)新互聯(lián)
URL網(wǎng)址:http://www.rwnh.cn/article14/cepige.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、建站公司標簽優(yōu)化、面包屑導航用戶體驗、品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司
喜德县| 莎车县| 虞城县| 西昌市| 胶州市| 白沙| 勃利县| 凉山| 尉犁县| 青岛市| 建平县| 温宿县| 玛纳斯县| 丘北县| 仙游县| 星子县| 德兴市| 泰州市| 宣武区| 乃东县| 安达市| 吉安县| 台前县| 仙桃市| 广水市| 来安县| 肇源县| 平昌县| 武城县| 靖安县| 莱西市| 尖扎县| 揭东县| 博湖县| 集安市| 龙胜| 平江县| 桂平市| 乌审旗| 鄂伦春自治旗| 朔州市|