如何編寫(xiě)高性能.NET

這篇文章將為大家詳細(xì)講解有關(guān)如何編寫(xiě)高性能.NET,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

創(chuàng)新互聯(lián)建站是專業(yè)的茫崖網(wǎng)站建設(shè)公司,茫崖接單;提供網(wǎng)站設(shè)計(jì)制作、成都做網(wǎng)站,網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行茫崖網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!

減少分配率

這個(gè)幾乎不用解釋,減少了內(nèi)存的使用量,自然就減少GC回收時(shí)的壓力,同時(shí)降低了內(nèi)存碎片與CPU的使用量。你可以用一些方法來(lái)達(dá)到這一目的,但它可能會(huì)與其它設(shè)計(jì)相沖突。

你需要在設(shè)計(jì)對(duì)象時(shí)仔細(xì)檢查每個(gè)它并問(wèn)自己:

  1. 我真的需要這個(gè)對(duì)象嗎?

  2. 這個(gè)字段是我需要的嗎?

  3. 我能減少數(shù)組的尺寸嗎?

  4. 我能縮小primitives的尺寸嗎(用Int32替換Int64,其它)?

  5. 這些對(duì)象,是否只有在極少數(shù)情況下,或者只有初始化的時(shí)候才用到?

  6. 是否能將一些類轉(zhuǎn)為結(jié)構(gòu)體使他們?cè)跅I戏峙浠蛘叱蔀槟硞€(gè)對(duì)象的一部分?

  7. 我是否分配了大量?jī)?nèi)存,但實(shí)際只使用其中很小的一部分?

  8. 我可以從其它地方拿到相關(guān)數(shù)據(jù)?

小故事:在服務(wù)端一個(gè)響應(yīng)請(qǐng)求的函數(shù)里,我們發(fā)現(xiàn)在一次請(qǐng)求里會(huì)分配一些比內(nèi)存段要大的內(nèi)存。這樣導(dǎo)致每次請(qǐng)求我們都會(huì)觸發(fā)一次完整的GC,這是因?yàn)镃LR要求所有的0代對(duì)象都在一個(gè)內(nèi)存段里,當(dāng)前分配的內(nèi)存段滿了,就會(huì)開(kāi)辟一個(gè)新的內(nèi)存段,同時(shí)對(duì)原先的內(nèi)存段做一次2代的回收。這不是一個(gè)好的實(shí)現(xiàn),因?yàn)槲覀兂藴p少內(nèi)存分配外別無(wú)它法。

最重要的規(guī)則

對(duì)于垃圾回收的高性能編程有一個(gè)基本規(guī)則,事實(shí)上也是代碼設(shè)計(jì)的指導(dǎo)規(guī)則。

要收集的對(duì)象要么在0代,要么不存在
Collect objects in gen 0 or not at all.

不同的是,你希望一個(gè)對(duì)象擁有極短的生命周期,在GC的時(shí)候永遠(yuǎn)不要碰到它,或者,如果你做不到這一點(diǎn),它們應(yīng)該去2代,盡可能的快,永遠(yuǎn)的呆在那,永遠(yuǎn)不會(huì)被回收。這意味著你永遠(yuǎn)保持對(duì)長(zhǎng)生命周期對(duì)象的引用。通常,也意味著對(duì)象可重復(fù)使用,尤其是在大對(duì)象堆里的對(duì)象。
GC每高一個(gè)世代的回收會(huì)比上一個(gè)世代更加耗時(shí)。如果你想保持許多0,1代和少量的2代對(duì)象。即使開(kāi)啟后臺(tái)GC做2代做回收,也會(huì)消耗相當(dāng)CPU運(yùn)算量,你可能更愿意將這部分的CPU消耗給應(yīng)用程序,而不是GC。

Note 你可能聽(tīng)過(guò)一個(gè)說(shuō)法,每10次0代的回收會(huì)產(chǎn)生一次1代的回收,每10次1代的回收會(huì)產(chǎn)生1次2代的回收。這其實(shí)是不正確的,但是你要明白,你要盡可能產(chǎn)生多次快速的0代回收,以及少量的2代回收。

你最好避免進(jìn)行1代回收,主要是因?yàn)橐呀?jīng)從0代提升到1代的對(duì)象,會(huì)在這時(shí)候被轉(zhuǎn)入2代。1代是對(duì)象進(jìn)入2代的一個(gè)緩沖區(qū)。
理想情況下,你分配的每一個(gè)對(duì)象應(yīng)該在下一次0代回收前結(jié)束生命周期。你可以測(cè)量?jī)纱蜧C的時(shí)間間隔,并將其與應(yīng)用程序里對(duì)象的生命周期長(zhǎng)度做對(duì)比。有關(guān)如何使用工具測(cè)量生命周期的信息,可以在本章結(jié)尾看到。
你可能不習(xí)慣這樣思考,但這規(guī)則切入了應(yīng)用程序的方方面面,你需要經(jīng)常思考它,在心態(tài)要做根本的轉(zhuǎn)變,這樣才能實(shí)現(xiàn)這個(gè)最重要的規(guī)則。

縮短對(duì)象的生命周期

一個(gè)對(duì)象的作用范圍越短,在下一個(gè)GC出現(xiàn)時(shí),它被提升到下一代的機(jī)會(huì)就越小。一般來(lái)說(shuō),在你需要之前,不要?jiǎng)?chuàng)建對(duì)象。
同時(shí),當(dāng)對(duì)象創(chuàng)建的代價(jià)如此之高時(shí),異常就可以在較早的時(shí)候創(chuàng)建,這樣不會(huì)干擾到其他處理邏輯。
另外,你還要確保對(duì)象盡可能早的退出作用域。對(duì)于局部變量,你可以在最后一次使用后,甚至在方法結(jié)束前將其生命周期結(jié)束。你可一個(gè)用{}將代碼包括起來(lái),這不會(huì)對(duì)你的運(yùn)行產(chǎn)生影響,但編譯器會(huì)認(rèn)為在這個(gè)范圍的對(duì)象已經(jīng)完成了他的生命周期,不再被使用了。如果需要調(diào)用對(duì)象的方法,盡量減少第一次和最后一次的時(shí)間間隔,以便GC盡早的回收對(duì)象。
如果對(duì)象關(guān)聯(lián)(引用)了一些會(huì)長(zhǎng)時(shí)間保持的對(duì)象,則需要解除他們的引用關(guān)系。你可能會(huì)有更多的空值檢查(null判斷),這可能會(huì)讓代碼變得更復(fù)雜。也會(huì)在對(duì)象的可用狀態(tài)(always having full state available)上與效率之間造成緊張關(guān)系,特別是調(diào)試的時(shí)候。
解決的一種方法是,將要清空的對(duì)象轉(zhuǎn)換為另外一種方式存在,例如:日志消息,這樣在后面的調(diào)試時(shí)可以查詢到相關(guān)信息。
另外一種方法是為代碼增加可配置選項(xiàng)(不解除對(duì)象之間的關(guān)系):運(yùn)行程序(或者運(yùn)行程序里特定的一個(gè)部分,例如一個(gè)特定的請(qǐng)求),在這個(gè)模式中沒(méi)有解除對(duì)象引用關(guān)系,而是盡可能讓對(duì)象一直保持方便調(diào)試。

減少對(duì)象層次的深度

如本章開(kāi)頭所述,GC在回收時(shí)會(huì)順著對(duì)象的引用關(guān)系進(jìn)行遍歷。在服務(wù)器GC模式,GC會(huì)以多線程方式運(yùn)行,但如果一個(gè)線程需要處理一個(gè)對(duì)象層次很深,則所有已經(jīng)處理完的線程都需要等這個(gè)線程完成處理后才能退出。在今后的CLR版本里,你可以不用太關(guān)注這個(gè)問(wèn)題,GC在多線程執(zhí)行時(shí)會(huì)采用更好的標(biāo)記算法做負(fù)載均衡。但如果你對(duì)象層次很深,這個(gè)問(wèn)題還是要關(guān)注一下的。

減少對(duì)象之間的引用

這與上節(jié)的深度有關(guān),但也有一些其它的因素。
一個(gè)對(duì)象如果引用了很多對(duì)象(數(shù)組,List吧),那它將花很多時(shí)間在遍歷對(duì)象上。是GC造成長(zhǎng)時(shí)間的一個(gè)問(wèn)題,因?yàn)樗幸粋€(gè)復(fù)雜的關(guān)系圖。
另外一個(gè)問(wèn)題是,如果無(wú)法輕松的確定對(duì)象有多少引用關(guān)系,那么你就無(wú)法準(zhǔn)確的預(yù)測(cè)對(duì)象的生命周期。減少這種復(fù)雜度是相當(dāng)有必要的,它不但可以讓代碼更健壯,同時(shí)也方便調(diào)試以及獲得更好的性能。
另外,還要注意不同世代對(duì)象之間的引用也會(huì)導(dǎo)致GC的效率低下,特別是舊對(duì)象對(duì)新對(duì)象的引用。例如,如果2代對(duì)象在0代對(duì)象里有引用關(guān)系,那么每次發(fā)生0代的GC時(shí),也需要掃描部分2代對(duì)象,看看他們是否仍然保持到0代對(duì)象的引用上。雖然這不是一次完整的GC,但它仍然是不要的工作,你應(yīng)該盡量避免這種情況。

避免釘住對(duì)象(Pinning)

釘住對(duì)象可以保證從托管代碼往本地代碼里傳遞數(shù)據(jù)的安全。常見(jiàn)的有數(shù)組和字符串。如果你的代碼不需要與本地代碼做交互,則不用考慮它的性能開(kāi)銷。
釘住對(duì)象就是讓對(duì)象在垃圾回收(壓縮階段)時(shí)無(wú)法移動(dòng)他。雖然釘住對(duì)象不會(huì)造成多大開(kāi)銷,但它會(huì)妨礙到GC的回收操作,增加內(nèi)存碎片的可能性。GC在回收時(shí)會(huì)記錄對(duì)象的位置,以便在重修分配時(shí)利用它們之間的空間,但如果釘住的對(duì)象很多,會(huì)導(dǎo)致內(nèi)存碎片的增加。
釘可以是顯示的也可以使隱式的。顯示的是使用GCHandle時(shí)用GCHandleType.Pinned參數(shù)進(jìn)行設(shè)置,或者在unsafe模式下使用 fixed 關(guān)鍵字。使用fixed關(guān)鍵字和GCHandle的差別在于是否會(huì)顯示調(diào)用Dispose方法。使用fixed雖然很方便,但是不能在異步情況下使用,但還是可以創(chuàng)建一個(gè)句柄對(duì)象(GCHandle),在回調(diào)時(shí)傳回并處理。
隱式的釘住對(duì)象則比較常見(jiàn),但也更難排查,也更難移除。最明顯的例子就是通過(guò)平臺(tái)調(diào)用(P/Invoke)將對(duì)象傳遞給非托管代碼。這不僅僅是你的代碼—--你經(jīng)常調(diào)用的一些托管API,實(shí)際上也是會(huì)調(diào)用本地代碼,也會(huì)將對(duì)象釘住。
CLR也會(huì)將自己的一些數(shù)據(jù)給釘住,但這通常不需要你來(lái)關(guān)心。
理想情況下,你應(yīng)該盡可能的不要釘住對(duì)象。如果不能做到,那么遵循之前的重要規(guī)則,盡可能讓這些被釘?shù)膶?duì)象盡早釋放。如果對(duì)象只是簡(jiǎn)單的被釘住后釋放,那么也不會(huì)有多少機(jī)會(huì)影響回收操作。你同時(shí)也要避免同時(shí)釘住很多個(gè)對(duì)象。被釘?shù)膶?duì)象被交換到2代或者在LOH里分配會(huì)稍微好些。根據(jù)這個(gè)規(guī)則,你可以在大對(duì)象堆上分配一個(gè)大的緩沖區(qū),并根據(jù)實(shí)際需自己對(duì)緩沖區(qū)做管理?;蛘咴谛?duì)象對(duì)上分配緩沖區(qū),然后在釘住他們前,使他們升級(jí)到2代。這樣比你直接將對(duì)象釘在0代上要好。

關(guān)于“如何編寫(xiě)高性能.NET”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

網(wǎng)頁(yè)名稱:如何編寫(xiě)高性能.NET
鏈接分享:http://muchs.cn/article0/ghogio.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、企業(yè)網(wǎng)站制作、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)站策劃商城網(wǎng)站

廣告

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

成都定制網(wǎng)站建設(shè)