国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > .NET > 正文

由一個性能問題引出的.net概念

2024-07-10 13:00:01
字體:
來源:轉載
供稿:網友

最大的網站源碼資源下載站,

由一個性能問題引出的.net概念
關鍵字:.net 性能 gc 值類型 引用類型 堆 堆棧 string

1 引子
我們先來看一下兩組代碼,每組中的哪一段代碼效率更高呢?

第一組:

代碼1:

for(int i = 0; i < 10000; i++)

{

addressdata ds = new addresssdata();

ds = addresss.getaddress();

}



代碼2:

for(int i = 0; i < 10000; i++)

{

addressdata ds;

ds = addresss.getaddress();

}

第二組:

代碼一:

string strnames = "@"+guid.newguid().tostring().replace("-","")+ ",@"+guid.newguid().tostring().replace("-","")+ ",@"+guid.newguid().tostring().replace("-","");

for(int i = 0; i < 10000; i++)

{

……

}



代碼2:

for(int i = 0; i < 10000; i++)

{

string strnames = "@"+guid.newguid().tostring().replace("-","")+ ",@"+guid.newguid().tostring().replace("-","")+ ",@"+guid.newguid().tostring().replace("-","");

……

}

每一組代碼中,兩段代碼實現的功能是一樣的,它們之間的區別也很小,但是其效率會相差得驚人,其中一種會很頻繁的gc,為什么呢?

在回答這個問題的時候我們先了解幾個.net概念。

2 什么是gc
gc的全稱是garbage collection,中文名稱垃圾回收,是.net中對內存管理的一種功能。垃圾回收器跟蹤并回收托管內存中分配的對象,定期執行垃圾回收以回收分配給沒有有效引用的對象的內存。當使用可用內存不能滿足內存請求時,gc會自動進行。

在進行垃圾回收時,垃圾回收器回首先搜索內存中的托管對象,然后從托管代碼中搜索被引用的對象并標記為有效,接著釋放沒有被標記為有效的對象并收回內存,最后整理內存將有效對象挪動到一起。這就是gc的四個步驟。

由上可見,gc是很影響性能的,所以一般說來這種事情況還是盡量少發生為好。

為了減少一些性能影響,.net的gc支持對象老化,或者說分代的概念,代是對象在內存中相對存現時期的度量單位,對象的代數或存現時期說明對象所屬的代。目前.net的垃圾回收器支持三代。每進行一次gc,沒有被回收的對象就自動提升一代。較近創建的對象屬于較新的代,比在應用程序生命周期中較早創建的對象的代數低。最近代中的對象位于零代中。每一次gc的時候,都首先回收零代中的對象,只有在較低代數的對象回收完成后仍不能滿足需求的情況下才回收較高代數的對象。

3 堆棧和堆
內存有堆棧和堆的概念。堆棧遵循后進先出的原則,后被推入堆棧的對象必定實現本拉出堆棧,這樣保證了這部分內存的緊湊,也基本上不需要考慮內存地址的問題。而堆則沒有這個原則,任何一個對象都有可能在任何時候進入堆中,也可能在任何時候被移出堆。這樣很明顯,我們就要考慮每一個對象保存在哪里,所以就需要在堆棧中保存每一個對象保存在堆中的地址。同時在經過一段時間之后我們就會發現堆中產生了許多空隙,也就是碎片,為了提高系統性能,我們這個時候經常需要整理堆,以清除碎片。關于堆棧和堆,如下圖所示:


4 gc和堆棧、堆
由前述堆棧和堆的概念可以看出,堆棧不存在垃圾收集的問題,只需要直接壓棧即可,而堆,則面臨著很復雜的垃圾回收的問題。gc完全是對堆進行操作的,而對堆中對象是否有效的判斷則是通過遍歷堆棧來實現的。這里涉及到一個引用計數的概念,引用計數是對堆中對象被引用次數的統計,當一個對象的引用計數為零了,那么這個對象就可以被回收了。在進行gc的時候,垃圾回收器遍歷堆棧,當發現一個堆地址的時候,它就將堆中該地址上的對象的引用計數加1,然后銷毀堆中所有引用計數為零的對象,回收內存并整理堆中的碎片。

5 值類型和引用類型
我們都知道,計算機中的數據類型分為值類型和引用類型兩種。那么到底什么是值類型什么是引用類型呢?

大多數編程語言提供內置的數據類型(比如整數和浮點數),這些數據類型會在作為參數傳遞時被復制(即,它們通過值來傳遞)。在 .net framework 中,這些稱為值類型。運行庫支持兩種值類型:內置值類型和用戶定義的值類型。

引用類型則存儲對值的內存地址的引用。引用類型可以是自描述類型、指針類型或接口類型。引用類型的類型可以由自描述類型的值來確定。自描述類型進一步細分成數組和類類型。類類型是用戶定義的類、裝箱的值類型和委托。

作為值類型的變量,每個都有自己的數據副本,因此對一個變量的操作不會影響其他變量。作為引用類型的變量可以引用同一對象;因此對一個變量的操作會影響另一個變量所引用的同一對象。

6 值類型、引用類型和堆棧、堆
了解了值類型和引用類型,那么,這兩種類型在內存中又是怎樣表現的呢?

值類型存儲在堆棧中,而引用類型則存儲在堆中,然后在堆棧中存儲對堆中對象的引用(又叫指針),如下圖所示:


因為這樣的一種存儲方式,于是就造成了對變量操作影響的不同,例如通過引用指針b對數據所作的更改也會表現在通過引用指針c的得到的數據中。而對值類型進行操作卻不會有這樣的情況。

這兩種不同也會表現在我們的方式上,例如:

我們假設modifyclass()方法是對classa中的字段value1加2;

classa ca = new classa();

ca.value1 = 2;

modifyclass(ca);

int getvalue = ca.value1;

……

這時你可以看到getvalue的值是4,而modifyclass()方法并沒有返回任何數據。

而對于值類型,我們就會發現這樣做是不可以的,你必須要讓方法有返回數據,如:

int ca = 2

int getvalue = modifyvalue(ca);

值類型和引用類型的區別還在于聲明新變量的時候,如classa ca = null是合法的,而int ca = null則是非法的。

7 類實例化的步驟
類是最常見也是我們用的最多的一種引用類型,我們知道實例化一個類使用的是一個我們司空見慣的語句:

classa ca = new classa();

那么這短短的一句話中,計算機又做了些什么事情呢?

實際上,計算機在這個過程中大致做了這么幾件事:

首先,在classa ca的時候,生成一個空的引用指針,并將它推入堆棧中:


然后,在new classa()的時候,生成classa的新的實例,并放入堆中:


在賦值號=這一步,將ca的引用指針指向剛剛生成的新實例:


這個時候,才算完成了整條語句的操作。

好了,了解了以上這些概念之后,我們可以來回答本文開始的問題了:

8 回答本文開始的問題
關于第一組代碼,我們首先要了解

addressdata ds = new addresssdata();

ds = addresss.getaddress();

在內存中的情況,我們已經知道,在上述代碼的第一句完成之后,會是這樣的一個樣子:


而在第二句代碼完成之后,就會變成這樣一個樣子:


其中classa的實例1是第代碼一句產生的,實例2是addresss.getaddress()方法產生的,實例2才是有效的對象,而實例1則不幸落了個一產生就只能等待著被垃圾回收器回收掉的命運。

一般而言,這種做法不會帶來太大的性能問題,但是在某些情況下呢?例如本文一開始演示的這樣一個循環?

這個時候就會在堆中產生非常多的垃圾,占用了大量的內存,于是不得不不斷的gc,從而嚴重影響性能。

那么對于第二組代碼又會怎樣呢?

看起來,第二組代碼和第一組代碼很不一樣,一個是類這種典型的引用類型,而一種是字符串這種通常看起來像是值類型的東西。

實際上,字符串是一種很特殊的東東,它兼有值類型和引用類型的特征,例如我們必須用這樣的方式對他進行處理:

string ds = “this is a test”;

ds = modifystring(ds);

其中需要注意的是第二句,這是典型的對值類型進行操作的方式,modifystring()方法必須要返回數據;但另一方面,我們初始化一個新的字符串變量的時候卻又可以這樣寫:string ds = null。很奇怪對吧,我感覺,之所以會出現這種情況的原因可能是設計者希望它能盡量的和其他的值類型如int,float等數據類型的使用方式一致,畢竟它本身的特征和給我們的感覺是如此的相似,但它卻又是無法固定長度的,int和float等等我們都明確的規定了它是多少位的,string卻不行。

由于這個原因,造成了第二組兩段代碼之間的性能差異。

9 一些題外話:一定要用class嗎?
我們知道class是引用類型的,struct則是一種值類型,class是面向對象編程時代所特有的,而我們稱作結構的struct則只是面向對象編程萌芽階段出現的一個實現對象化的一個變通做法,以至于java就拋棄了struct的概念,那么.net為什么沒有放棄java放棄了的東西呢?

struct不是垃圾。首先,struct是一種值類型,這樣使得它可以存放在堆棧中,而不是堆中,也就是說,它不會帶來gc的性能影響;其次,例如一個對象中有100個元素,如果把這個對象分別定義成類和結構各會占用多少內存空間呢?答案是,定義為類會占用101塊內存空間,分別是100個元素和引用指針的;而定義成結構只占用100塊而已。也許你會說,才多占用這么一塊沒什么關系,不過增加了1%而已,那么,如果這個對象只有三個元素呢?

所以我們可以很明白的得出結論,class不是唯一。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 孝感市| 内乡县| 和政县| 温泉县| 玉山县| 长沙市| 黄骅市| 潜山县| 建水县| 麻阳| 萍乡市| 剑河县| 石屏县| 金塔县| 扎囊县| 南昌市| 专栏| 礼泉县| 闽清县| 清水河县| 洪泽县| 新河县| 青岛市| 乌鲁木齐县| 鸡东县| 百色市| 秦皇岛市| 磴口县| 松溪县| 响水县| 通城县| 龙门县| 哈密市| 黄骅市| 平武县| 友谊县| 桦川县| 芦山县| 府谷县| 金川县| 安阳市|