您的位置:首页 > 其它

[经典文章翻译] [未完工] [9-6更新] 在.NET Framework中针对Real-Time技术的性能注意事项

2011-08-16 16:14 441 查看
原作者:EmmanuelSchanzer

 

总结:

这篇文章包括了在托管世界中工业级的各种技术以及对它们如何影响性能的技术解释.涉及到垃圾收集,JIT,Remoting,ValueTypes,安全等方面.

 

概览:

.NET运行时引入了多种旨在提高安全性,易开发性,高性能的高级技术.作为一个研发人员,理解这些技术的任何一个并在你的代码中高效地使用这些技术都是比较重要的.RunTime提供的高级工具会使得创建健壮的应用程序变得更加容易,但是如何让应用程序飞的更快一点却是(也一直是)研发人员的责任.

 

这篇白皮书会给你提供一个对.NET的工业级技术的更加深入的理解,并帮助你调整你的代码使之运行的更快.注意,这不是一篇规范表.现在已经有很多实实在在的技术信息了.这篇文章的目的是聚焦性能问题来提供信息,也许不能回答你的每一个技术问题.如果这里找不到你的问题的答案,我建议你再在MSDN在线文档库中在多看看.

 

我将会讨论到下面的技术,并且对它们的目的以及为什么他们会影响性能提供高层次的概述.之后我会深入到一些底层的技术实现细节,并使用示例代码来说明如何从每一项techonlgy中获取高性能和高速度.

GarbageCollection
ThreadPool
TheJIT
AppDomains
Security
Remoting
ValueTypes

 

垃圾收集

=======================

基础

垃圾收集(GC)通过释放不再被使用的对象的内存把程序员从释放内存这种常见却又难以debug其错误的任务中解放了出来.一般一个对象的生存路径如下代码所示,托管或非托管是一样的:

Fooa=newFoo();//为对象分配内存并初始化
...a...//使用该对象
deletea;//清除对象的状态,进行清理
//释放这个对象的内存


 

在nativecode中,你需要自己来做所有的这一切.忽略内存分配阶段或清理阶段都会导致不可预期的行为,并且这种问题难以debug,忘记释放内存则会导致内存泄露.在CLR,内存分配跟我们刚刚看到的很接近.如果我们添加GC-specific信息的话,我们会得到看起来非常相似的内容.

Fooa=newFoo();//为对象分配内存并初始化
...a...//使用该对象(该对象是strongreachable的)
a=null;//A对象变为unreachable了(outofscope,nulled,等等)
//最终,对A对象的回收发生,还有同时还要回收A的资源.
//内存被回收掉


 

直到对象能够被释放之前,在托管和非托管世界以上的步骤都是一样的.在nativecode,你需要记住释放在对象使用完毕后释放掉它.在managedcode,一旦对象变为unreachable,那么GC会回收它.当然了,如果你的resource需要在释放上特别吃小灶的话(比如说关闭socket),GC就需要你的帮助才能正确地进行处理.你写的代码中,在对象释放之前进行清理工作这一条规则依然适用,你可以使用Dispose()Finalize()方法来这样做.我们稍后会谈论这二者的区别.

 

如果你保留着一个指向某资源的指针,那么GC就不可能知道你是否将来还要使用这项资源.这就意味着你在nativecode中使用的所有的显式释放对象的规则仍然适用,但是绝大多数情况下,GC会为你处理掉一切.如果说以前你要把百分一百的时间投入到内存管理上,那么现在你仅需要百分之五的时间来考虑内存管理了.

 

CLR的垃圾收集器是一个按代划分的(generational),标记并整理的(mark-and-compact)回收器.它遵循以下的几条原则,这些原则能让它获得出色的性能.首先,短命的对象往往是较小的和会经常被访问到的.GC把分配图表划分为几个子图表,叫做generations(代),Generation能让GC尽可能地花费较少的时间来进行回收.Gen0包含年轻的,经常被访问的对象.这些对象规模趋近于最小,并且需要大概10毫秒来回收.因为GC能够再进行这次回收的时候忽略其他generation的回收,所以它可以提供更高的性能.G1和G2是为了更大的,更老的,不会被频繁回收的对象准备的.当G1回收发生的时候,G0也被回收.G2的回收是一种完全的回收,尽在这是GC会遍历整个内存graph.它还会智能地使用CPU缓存,通过这种技术能够调整某个CPU之上的内存子系统.对于native的内存分配来说,这种优化不容易获得,但如果有这种优化的话,就能够帮助提高你的应用程序的性能.

 

垃圾收集何时发生?

在需要分配内存的时候,GC会检查是否需要进行回收.GC会查看可回收的内存的大小,剩下的内存的大小,以及每一个generation的大小,然后使用一个启发式方法来做决定.直到一个回收发生,对象的内存分配可以像C或C++一样快,甚至更快.

 

垃圾收集的时候做发生了些什么?

让我们一步步地看垃圾收集器在回收的时候都做了哪些步骤吧.GC维护着一个root的列表,该列表内容指向GC的堆heap.如果一个对象是活动的,那么就会有一个root指向它在堆中的位置.堆中的对象还可以互相引用.这张指针图(reachabilitygraph)是GC为了释放内存而必须进行搜索的.事件发生的顺序如下:

1.托管堆中所有的内存分配块都是连续的,当剩下的一块大小不足以应付一个请求的时候,那么GC就会被触发了.

2.GC顺着每一个root以及root之后的所有指针进行遍历,生成一个列表,列表中的对象都是前面的遍历所无法到达的.

3.从root出发进行遍历,每一个无法到达的对象都被认为是可以回收的,并且这些对象会为后面的回收而被进行标记.

             



4.从reachabilitygraph中移除掉对象,使得很多对象都可以回收了.然而,有些资源需要进行特别处理.当你定义一个对象的时候,你可以选择为它定义Dispose()方法或Finalize()方法,或者二者都有.我们稍后会讨论这二者的不同,并且会讨论什么时候使用它们.

5.回收的最后一步是内存整理阶段.所有正在被使用的对象都被移到一块连续的内存块上,所有的指针以及root都会被更新.

6.通过整理活动的对象并且更新可用内存的起始地址,GC保持了内用内存快的连续性.如果有足够空间进行内存分配,那么GC就会把控制转交给应用程序.如果还不能满足,那么就报出exception,类型为
OutOfMemoryException


            



 

ObjectCleanup

Someobjectsrequirespecialhandlingbeforetheirresourcescanbereturned.Afewexamplesofsuchresourcesarefiles,networksockets,ordatabaseconnections.Simplyreleasingthememoryontheheapisn'tgoingtobeenough,sinceyouwanttheseresourcesclosedgracefully.Toperformobjectcleanup,youcanwriteaDispose()method,aFinalize()method,orboth.

AFinalize()method:

IscalledbytheGC

Isnotguaranteedtobecalledinanyorder,oratapredictabletime

Afterbeingcalled,freesmemoryafterthenextGC

KeepsallchildobjectsliveuntilthenextGC

ADispose()method:

Iscalledbytheprogrammer

Isorderedandscheduledbytheprogrammer

Returnsresourcesuponcompletionofthemethod

Managedobjectsthatholdonlymanagedresourcesdon'trequirethesemethods.Yourprogramwillprobablyuseonlyafewcomplexresources,andchancesareyouknowwhattheyareandwhenyouneedthem.Ifyouknowbothofthesethings,there'snoreasontorelyonfinalizers,sinceyoucandothecleanupmanually.Thereareseveralreasonsthatyouwanttodothis,andtheyallhavetodowiththefinalizerqueue.

IntheGC,whenanobjectthathasafinalizerismarkedcollectable,itandanyobjectsitpointstoareplacedinaspecialqueue.Aseparatethreadwalksdownthisqueue,callingtheFinalize()methodofeachiteminthequeue.Theprogrammerhasnocontroloverthisthread,ortheorderofitemsplacedinthequeue.TheGCmayreturncontroltotheprogram,withouthavingfinalizedanyobjectsinthequeue.Thoseobjectsmayremaininmemory,tuckedawayinqueueforalongtime.Callstofinalizearedoneautomatically,andthereisnodirectperformanceimpactfromcallitself.However,thenon-deterministicmodelforfinalizationcandefinitelyhaveotherindirectconsequences:

Inascenariowhereyouhaveresourcesthatneedtobereleasedataspecifictime,youlosecontrolwithfinalizers.Sayyouhaveafileopen,anditneedstobeclosedforsecurityreasons.Evenwhenyousettheobjecttonull,andforceaGCimmediately,thefilewillremainopenuntilitsFinalize()methodiscalled,andyouhavenoideawhenthiscouldhappen.

Nobjectsthatrequiredisposalinacertainordermaynotbehandledcorrectly.

Anenormousobjectanditschildrenmaytakeupfartoomuchmemory,requireadditionalcollectionsandhurtperformance.Theseobjectsmaynotbecollectedforalongtime.

Asmallobjecttobefinalizedmayhavepointerstolargeresourcesthatcouldbefreedatanytime.Theseobjectswillnotbefreeduntiltheobjecttobefinalizedistakencareof,creatingunnecessarymemorypressureandforcingfrequentcollections.

ThestatediagraminFigure3illustratesthedifferentpathsyourobjectcantakeintermsoffinalizationordisposal.





Asyoucansee,finalizationaddsseveralstepstotheobject'slifetime.Ifyoudisposeofanobjectyourself,theobjectcanbecollectedandthememoryreturnedtoyouinthenextGC.Whenfinalizationneedstooccur,youhavetowaituntiltheactualmethodgetscalled.Sinceyouarenotgivenanyguaranteesaboutwhenthishappens,youcanhavealotofmemorytiedupandbeatthemercyofthefinalizationqueue.Thiscanbeextremelyproblematicifyourobjectisconnectedtoawholetreeofobjects,andtheyallsitinmemoryuntilfinalizationoccurs.

 

ChoosingWhichGarbageCollectortoUse

TheCLRhastwodifferentGCs:Workstation(mscorwks.dll)andServer(mscorsvr.dll).WhenrunninginWorkstationmode,latencyismoreofaconcernthanspaceorefficiency.Aserverwithmultipleprocessorsandclientsconnectedoveranetworkcanaffordsomelatency,butthroughputisnowatoppriority.RatherthanshoehornbothofthesescenariosintoasingleGCscheme,Microsofthasincludedtwogarbagecollectorsthataretailoredtoeachsituation.

ServerGC:

Multiprocessor(MP)Scalable,Parallel

OneGCthreadperCPU

Programpausedduringmarking

WorkstationGC:

Minimizespausesbyrunningconcurrentlyduringfullcollections

TheserverGCisdesignedformaximumthroughput,andscaleswithveryhighperformance.Memoryfragmentationonserversisamuchmoresevereproblemthanonworkstations,makinggarbagecollectionanattractiveproposition.Inauniprocessorscenario,bothcollectorsworkthesameway:workstationmode,withoutconcurrentcollection.OnanMPmachine,theWorkstationGCusesthesecondprocessortorunthecollectionconcurrently,minimizingdelayswhilediminishingthroughput.TheServerGCusesmultipleheapsandcollectionthreadstomaximizethroughputandscalebetter.

YoucanchoosewhichGCtousewhenyouhosttheruntime.Whenyouloadtheruntimeintoaprocess,youspecifywhatcollectortouse.LoadingtheAPIisdiscussedinthe.NETFrameworkDeveloper'sGuide.ForanexampleofasimpleprogramthathoststheruntimeandselectstheserverGC,takealookattheAppendix.

Myth:GarbageCollectionIsAlwaysSlowerThanDoingItbyHand

Actually,untilacollectioniscalled,theGCisalotfasterthandoingitbyhandinC.Thissurprisesalotofpeople,soit'sworthsomeexplanation.Firstofall,noticethatfindingfreespaceoccursinconstanttime.Sinceallfreespaceiscontiguous,theGCsimplyfollowsthepointerandcheckstoseeifthere'senoughroom.InC,acalltomalloc()
typically
resultsinasearchofalinkedlistoffreeblocks.Thiscanbetimeconsuming,especiallyifyourheapisbadlyfragmented.Tomakemattersworse,severalimplementationsoftheCruntimelocktheheapduringthisprocedure.Oncethememoryisallocatedorused,thelisthastobeupdated.Inagarbage-collectedenvironment,allocationisfree,andthememoryisreleasedduringcollection.Moreadvancedprogrammerswillreservelargeblocksofmemory,andhandleallocationwithinthatblockthemselves.Theproblemwiththisapproachisthatmemoryfragmentationbecomesahugeproblemforprogrammers,anditforcesthemtoaddalotofmemory-handlinglogictotheirapplications.Intheend,agarbagecollectordoesn'taddalotofoverhead.Allocationisasfastorfaster,andcompactionishandledautomatically—freeingprogrammerstofocusontheirapplications.

Inthefuture,garbagecollectorscouldperformotheroptimizationsthatmakeitevenfaster.Hotspotidentificationandbettercacheusagearepossible,andcanmakeenormousspeeddifferences.AsmarterGCcouldpackpagesmoreefficiently,therebyminimizingthenumberofpagefetchesthatoccurduringexecution.Allofthesecouldmakeagarbage-collectedenvironmentfasterthandoingthingsbyhand.

SomepeoplemaywonderwhyGCisn'tavailableinotherenvironments,likeCorC++.Theansweristypes.Thoselanguagesallowcastingofpointerstoanytype,makingitextremelydifficulttoknowwhatapointerrefersto.InamanagedenvironmentliketheCLR,wecanguaranteeenoughaboutthepointerstomakeGCpossible.ThemanagedworldisalsotheonlyplacewherewecansafelystopthreadexecutiontoperformaGC:inC++theseoperationsareeitherunsafeorverylimited.

TuningforSpeed

Thebiggestworryforaprograminthemanagedworldismemoryretention.Someoftheproblemsthatyou'llfindinunmanagedenvironmentsarenotanissueinthemanagedworld:memoryleaksanddanglingpointersarenotmuchofaproblemhere.Instead,programmersneedtobecarefulaboutleavingresourcesconnectedwhentheynolongerneedthem.

Themostimportantheuristicforperformanceisalsotheeasiestonetolearnforprogrammerswhoareusedtowritingnativecode:keeptrackoftheallocationstomake,andfreethemwhenyou'redone.TheGChasnowayofknowingthatyouaren'tgoingtousea20KBstringthatyoubuiltifit'spartofanobjectthat'sbeingkeptaround.Supposeyouhavethisobjecttuckedawayinavectorsomewhere,andyouneverintendtousethatstringagain.SettingthefieldtonullwilllettheGCcollectthose20KBlater,evenifyoustillneedtheobjectforotherpurposes.Ifyoudon'tneedtheobjectanymore,makesureyou'renotkeepingreferencestoit.(Justlikeinnativecode.)Forsmallerobjects,thisislessofaproblem.Anyprogrammerthat'sfamiliarwithmemorymanagementinnativecodewillhavenoproblemhere:allthesamecommonsenserulesapply.Youjustdon'thavetobesoparanoidaboutthem.

Thesecondimportantperformanceconcerndealswithobjectcleanup.AsImentionedearlier,finalizationhasprofoundimpactsonperformance.Themostcommonexampleisthatofamanagedhandlertoanunmanagedresource:youneedtoimplementsomekindofcleanupmethod,andthisiswhereperformancebecomesanissue.Ifyoudependonfinalization,youopenyourselfuptotheperformanceproblemsIlistedearlier.SomethingelsetokeepinmindisthattheGCislargelyunawareofmemorypressureinthenativeworld,soyoumaybeusingatonofunmanagedresourcesjustbykeepingapointeraroundinthemanagedheap.Asinglepointerdoesn'ttakeupalotofmemory,soitcouldbeawhilebeforeacollectionisneeded.Togetaroundtheseperformanceproblems,whilestillplayingitsafewhenitcomestomemoryretention,youshouldpickadesignpatterntoworkwithforalltheobjectsthatrequirespecialcleanup.

Theprogrammerhasfouroptionswhendealingwithobjectcleanup:

1.ImplementBoth

Thisistherecommendeddesignforobjectcleanup.Thisisanobjectwithsomemixofunmanagedandmanagedresources.AnexamplewouldbeSystem.Windows.Forms.Control.Thishasanunmanagedresource(HWND)andpotentiallymanagedresources(DataConnection,etc.).Ifyouareunsureofwhenyoumakeuseofunmanagedresources,youcanopenthemanifestforyourprogramin
ILDASM
andcheckforreferencestonativelibraries.Anotheralternativeistouse
vadump.exe
toseewhatresourcesareloadedalongwithyourprogram.Bothofthesemayprovideyouwithinsightastowhatkindofnativeresourcesyouuse.

Thepatternbelowgivesusersasinglerecommendedwayinsteadofoverridingcleanuplogic(overrideDispose(bool)).Thisprovidesmaximumflexibility,aswellascatch-alljustincaseDispose()isnevercalled.Thecombinationofmaximumspeedandflexibility,aswellasthesafety-netapproachmakethisthebestdesigntouse.

Example:


publicclassMyClass:IDisposable{publicvoidDispose(){Dispose(true);GC.SuppressFinalizer(this);}protectedvirtualvoidDispose(booldisposing){if(disposing){...}...}~MyClass(){Dispose(false);}}



 

2.ImplementDispose()Only

Thisiswhenanobjecthasonlymanagedresources,andyouwanttomakesurethatitscleanupisdeterministic.AnexampleofsuchanobjectisSystem.Web.UI.Control.

Example:


publicclassMyClass:IDisposable{publicvirtualvoidDispose(){...}



 

3.ImplementFinalize()Only

Thisisneededinextremelyraresituations,andIstronglyrecommendagainstit.TheimplicationofaFinalize()onlyobjectisthattheprogrammerhasnoideawhentheobjectisgoingtobecollected,yetisusingaresourcecomplexenoughtorequirespecialcleanup.Thissituationshouldneveroccurinawell-designedproject,andifyoufindyourselfinityoushouldgobackandfindoutwhatwentwrong.

Example:


publicclassMyClass{...~MyClass(){...}



 

4.ImplementNeither

Thisisforamanagedobjectthatpointsonlytoothermanagedobjectsthatarenotdisposablenortobefinalized.

 

Recommendation

Therecommendationsfordealingwithmemorymanagementshouldbefamiliar:releaseobjectswhenyou'redonewiththem,andkeepaneyeoutforleavingpointerstoobjects.Whenitcomestoobjectcleanup,implementbothaFinalize()andDispose()
methodforobjectswithunmanagedresources.Thiswillpreventunexpectedbehaviorlater,andenforcegoodprogrammingpractices

ThedownsidehereisthatyouforcepeopletohavetocallDispose().Thereisnoperformancelosshere,butsomepeoplemightfinditfrustratingtohavetothinkaboutdisposingoftheirobjects.However,Ithinkit'sworththeaggravationtouseamodelthatmakessense.Besides,thisforcespeopletobemoreattentivetotheobjectstheyallocate,sincetheycan'tblindlytrusttheGCtoalwaystakecareofthem.ForprogrammerscomingfromaCorC++background,forcingacalltoDispose()willprobablybebeneficial,sinceit'sthekindofthingtheyaremorefamiliarwith.

Dispose()shouldbesupportedonobjectsthatholdontounmanagedresourcesanywhereinthetreeofobjectsunderneathit;however,Finalize()needonlybeplacedonlyonthoseobjectsthatarespecificallyholdingontotheseresources,suchasanOSHandleorunmanagedmemoryallocation.Isuggestcreatingsmallmanagedobjectsas"wrappers"forimplementingFinalize()inadditiontosupportingDispose()
,
whichwouldbecalledbytheparentobject'sDispose().Sincetheparentobjectsdonothaveafinalizer,theentiretreeofobjectswillnotsurviveacollectionregardlessofwhetherornotDispose()wascalled.

Agoodruleofthumbforfinalizersistousethemonlyonthemostprimitiveobjectthatrequiresfinalization.SupposeIhavealargemanagedresourcethatincludesadatabaseconnection:Iwouldmakeitpossiblefortheconnectionitselftobefinalized,butmaketherestoftheobjectdisposable.ThatwayIcancallDispose()andfreethemanagedportionsoftheobjectimmediately,withouthavingtowaitfortheconnectiontobefinalized.Remember:useFinalize()onlywhereyouhaveto,whenyouhaveto.

NoteCandC++Programmers:theDestructorsemanticinC#createsafinalizer,notadisposalmethod!


 

线程池

=======================

 

 

JIT

=======================

 

 

AppDomain

=======================

 

 

Security

=======================

 

 

Remoting

=======================

 

 

ValueTypes

=======================

 

 

原文地址:

PerformanceConsiderationsforRun-TimeTechnologiesinthe.NETFramework

http://msdn.microsoft.com/en-us/library/ms973838
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐
章节导航