[Silverlight入门系列]MEF引起的内存泄露
2011-09-10 14:37
381 查看
也许你编程的时候很小心,注意不引起内存泄露,例如不要被全局Static的变量引用上,注意Singleton的static引用,注意EventHandler注销,注意IDisposable接口实现,而且正确实现了IDisposable。但或许你还是有内存泄露,为何?因为你的IDisposable接口根本没有被触发!为什么?参考MSDN这个页面的”Disposemethodnotinvoked”章节。还有其它的内存泄露原因,比如第三方组件或框架,框架本身的内存泄露问题,已经框架本身有LifetimeManagement对象生命周期管理机制。例如我今天要说的MEF引起的内存泄露。
MEF的对象生命周期管理机制说明,里面有这么一段话:
Containerandpartsreferences
Webelievethat.NetGarbageCollectoristhebestthingtorelyonforpropercleanup.However,wealsoneedtoprovideracontainerthathasadeterministicbehavior.Thus,thecontainerwillnotholdreferencestopartsitcreatesunlessoneofthefollowingistrue:
ThepartismarkedasShared
ThepartimplementsIDisposable
Oneormoreimportsisconfiguredtoallowrecomposition
Forthosecases,apartreferenceiskept.Combinedwiththefactthatyoucanhavenonsharedpartsandkeeprequestingthosefromthecontainerthenmemorydemandcanquicklybecomeanissue.Inordertomitigatethisissueyoushouldrelyononeofthefollowingstrategiesdiscussedinthenexttwotopics.
原来在MEF中,对象构造策略为Shared,实现IDisposable接口的,和允许recomposition的,都会被全局container引用,也就是说不释放。具体可以去看MEF的源码。
Windbg,RedgateANTSMemoryProfiler,以及.NETMemoryProfiler。我是用的Redgate的,大家可以从下面的图中看到为何这个ViewModel被引用着。根据这个图,去看MEF的源码,就ok了。
MEFPartsLifetime(zzfrom:
It’sveryimportantthatoneunderstandapartslifetimewithinaMEFcontaineranditsimplications.GiventhatMEFfocusesonopenendedapplicationsthisbecomesespeciallyimportantastheapplicationauthorswon’thavecontroloverthesetofpartsoncetheappshipsandthirdpartyextensionscometoplay.Lifetimecanbeexplainedasbeingthedesired“shareability”ofapart(transitively,itsexports)whichtranslatestothepolicythatcontrolswhenanewpartiscreatedaswellaswhenthepartwillbeshutdownordisposed.
Shared:thepartauthoristellingMEFthatatmostoneinstanceofthepartmayexistpercontainer.
NonShared:thepartauthoristellingMEFthateachrequestforexportsofthepartwillbeservedbyanewinstanceofit.
Anyornotsuppliedvalue:thepartauthorallowstheparttobeusedaseither“Shared”or“NonShared”.
TheCreationPolicycanbedefinedonapartusingthe[System.ComponentModel.Composition.PartCreationPolicyAttribute]:
[/code]
Thecontainerwillalwayshavetheownershipofpartsitcreates.Inotherwords,theownershipisnevertransferredtoanactorthatrequesteditbyusingthecontainerinstance(directly)orthroughanimport(indirectly).
Importscanalsodefineorconstraintthecreationpolicyofpartsusedtosupplytheimportvalues.AllyouhavetodoisspecifytheCreationPolicyenumvalueforRequiredCreationPolicy:
[/code]
Thisisausefulforscenarioswherethe“shareability”ofapartisrelevantfortheimporter.Bydefault,theRequiredCreationPolicyissettoAny,soSharedandNonSharedpartscansupplythevalues..
PartsthatimplementIDisposablewillhavetheDisposemethodcalled
Referencetopartsheldonthecontainerwillbecleanedup
Sharedpartswillbedisposedandcleanedup
Lazyexportswon’tworkafterthecontainerisdisposed
OperationsmightthrowSystem.ObjectDisposedException
ThepartismarkedasShared
ThepartimplementsIDisposable
Oneormoreimportsisconfiguredtoallowrecomposition
Forthosecases,apartreferenceiskept.Combinedwiththefactthatyoucanhavenonsharedpartsandkeeprequestingthosefromthecontainerthenmemorydemandcanquicklybecomeanissue.Inordertomitigatethisissueyoushouldrelyononeofthefollowingstrategiesdiscussedinthenexttwotopics.
Forthosescenariosyoushouldeitherusechildcontainers(explainedinthenexttopic)orreleaseearlytheobjectgraph.Thelatterallowsthecontainertodisposeandclearoutreferencestononsharedpartsintheobjectgraph–untilitreachesasharedpart.
InordertoearlyreleasetheobjectgraphyouneedtocallthemethodReleaseExportexposedbytheCompositionContainer:
[/code]
Thefigurebelowdepictsanobjectgraphandshowwhatpartswouldbereleased(referencesremoved,disposed)andtheonesthatwouldbeleftuntouched:
Astherootpartisjustnonsharednoreferencewasbeingkeptbythecontainer,soitisbasicallyano-operation.Weproceedtraversingthegraphcheckingtheexportsservedtotherootpart.Dep1isbothnonsharedanddisposable,sothepartisdisposedanditsreferenceisremovedfromthecontainer.ThesamehappenswithDep2,however,theexportusedbyDepisleftuntouchedasitisshared–sootherpartsmaybeusingit.
Notethattheimplementationtraversesthegraphinadepth-firstway.
Hence,whatyoushoulddoiseitherfiltertheparentcatalogbasedonacriterionthatdividesthesetofpartsthatshouldbecreatedontheparentcontainerfromthosethatshouldbecreatedonthechild,oryoushouldspecifyacompletelynewcatalogthatexposeasetofpartsthatshouldbecreatedonthechildcontainer.Asthechildisexpectedtobeshortlived,partscreatedinitwillbereleasedanddisposedearlier.
AcommonapproachistohaveSharedpartscreatedontheparentcontainerandNonSharedonthechildcontainer.AsSharedpartsmaydependonexportssuppliedbyNonShared,thenthemaincatalogwillhavetocontainthewholesetofpartswhereasthechildcontainershouldhaveafilteredviewofthemaincatalogwithonlythenonsharedparts.
FormoreinformationonthistopicpleasecheckFilteringCatalogs
[/code]
Note:thatMEFwillnevertakeownershipofaninstancesuppliedbyyou,butitdoeshavetheownershipofpartitcreatestosatisfyyourinstance’simports.
[/code]
实现IDisposable接口,却导致没有触发,这是何故?
看看Containerandpartsreferences
Webelievethat.NetGarbageCollectoristhebestthingtorelyonforpropercleanup.However,wealsoneedtoprovideracontainerthathasadeterministicbehavior.Thus,thecontainerwillnotholdreferencestopartsitcreatesunlessoneofthefollowingistrue:
ThepartismarkedasShared
ThepartimplementsIDisposable
Oneormoreimportsisconfiguredtoallowrecomposition
Forthosecases,apartreferenceiskept.Combinedwiththefactthatyoucanhavenonsharedpartsandkeeprequestingthosefromthecontainerthenmemorydemandcanquicklybecomeanissue.Inordertomitigatethisissueyoushouldrelyononeofthefollowingstrategiesdiscussedinthenexttwotopics.
原来在MEF中,对象构造策略为Shared,实现IDisposable接口的,和允许recomposition的,都会被全局container引用,也就是说不释放。具体可以去看MEF的源码。
内存泄露分析过程
内存分析工具很多,例如MEFPartsLifetime(zzfrom:http://mef.codeplex.com/wikipage?title=Parts%20Lifetime)
It’sveryimportantthatoneunderstandapartslifetimewithinaMEFcontaineranditsimplications.GiventhatMEFfocusesonopenendedapplicationsthisbecomesespeciallyimportantastheapplicationauthorswon’thavecontroloverthesetofpartsoncetheappshipsandthirdpartyextensionscometoplay.Lifetimecanbeexplainedasbeingthedesired“shareability”ofapart(transitively,itsexports)whichtranslatestothepolicythatcontrolswhenanewpartiscreatedaswellaswhenthepartwillbeshutdownordisposed.Shared,NonSharedandownership
The“shareability”ofapartisdefinedthroughtheCreationPolicyset(classlevel)usingthePartCreationPolicyAttribute.Thefollowingvaluesaresupported:Shared:thepartauthoristellingMEFthatatmostoneinstanceofthepartmayexistpercontainer.
NonShared:thepartauthoristellingMEFthateachrequestforexportsofthepartwillbeservedbyanewinstanceofit.
Anyornotsuppliedvalue:thepartauthorallowstheparttobeusedaseither“Shared”or“NonShared”.
TheCreationPolicycanbedefinedonapartusingthe[System.ComponentModel.Composition.PartCreationPolicyAttribute]:
[code][PartCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(IMessageSender))]
publicclassSmtpSender:IMessageSender
{
}
[/code]
Thecontainerwillalwayshavetheownershipofpartsitcreates.Inotherwords,theownershipisnevertransferredtoanactorthatrequesteditbyusingthecontainerinstance(directly)orthroughanimport(indirectly).
Importscanalsodefineorconstraintthecreationpolicyofpartsusedtosupplytheimportvalues.AllyouhavetodoisspecifytheCreationPolicyenumvalueforRequiredCreationPolicy:
[code][Export]
publicclassImporter
{
[Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
publicDependencyDep{get;set;}
}
[/code]
Thisisausefulforscenarioswherethe“shareability”ofapartisrelevantfortheimporter.Bydefault,theRequiredCreationPolicyissettoAny,soSharedandNonSharedpartscansupplythevalues..
Disposingthecontainer
Acontainerinstanceisgenerallythelifetimeholderofparts.Partinstancescreatedbythecontainerhavetheirlifetimeconditionedtothecontainer’slifetime.Thewaytosignaltheendofthecontainerlifetimeisbydisposingit.Theimplicationsofdisposingacontainerare:PartsthatimplementIDisposablewillhavetheDisposemethodcalled
Referencetopartsheldonthecontainerwillbecleanedup
Sharedpartswillbedisposedandcleanedup
Lazyexportswon’tworkafterthecontainerisdisposed
OperationsmightthrowSystem.ObjectDisposedException
Containerandpartsreferences
Webelievethat.NetGarbageCollectoristhebestthingtorelyonforpropercleanup.However,wealsoneedtoprovideracontainerthathasadeterministicbehavior.Thus,thecontainerwillnotholdreferencestopartsitcreatesunlessoneofthefollowingistrue:ThepartismarkedasShared
ThepartimplementsIDisposable
Oneormoreimportsisconfiguredtoallowrecomposition
Forthosecases,apartreferenceiskept.Combinedwiththefactthatyoucanhavenonsharedpartsandkeeprequestingthosefromthecontainerthenmemorydemandcanquicklybecomeanissue.Inordertomitigatethisissueyoushouldrelyononeofthefollowingstrategiesdiscussedinthenexttwotopics.
Scopedoperationsandearlyreclaimofresources
Somecommonkindsofapplicationslikewebappsandwindowsservicesvarygreatlyfromdesktopapplications.Theyaremorelikelytorelyonbatchedandshortlivedoperations.Forexample,awindowsservicemightwatchadirectlyandonceapre-determinednumberoffileispresent,startabatchingoperationthattransformsthosefilesintoanotherformat.Weboperationsmaybescopedbyper-web-requestoperations.Forthosescenariosyoushouldeitherusechildcontainers(explainedinthenexttopic)orreleaseearlytheobjectgraph.Thelatterallowsthecontainertodisposeandclearoutreferencestononsharedpartsintheobjectgraph–untilitreachesasharedpart.
InordertoearlyreleasetheobjectgraphyouneedtocallthemethodReleaseExportexposedbytheCompositionContainer:
[code]varbatchProcessorExport=container.GetExport<IBatchProcessor>();
varbatchProcessor=batchProcessorExport.Value;
batchProcessor.Process();
container.ReleaseExport(batchProcessorExport);
[/code]
Thefigurebelowdepictsanobjectgraphandshowwhatpartswouldbereleased(referencesremoved,disposed)andtheonesthatwouldbeleftuntouched:
Astherootpartisjustnonsharednoreferencewasbeingkeptbythecontainer,soitisbasicallyano-operation.Weproceedtraversingthegraphcheckingtheexportsservedtotherootpart.Dep1isbothnonsharedanddisposable,sothepartisdisposedanditsreferenceisremovedfromthecontainer.ThesamehappenswithDep2,however,theexportusedbyDepisleftuntouchedasitisshared–sootherpartsmaybeusingit.
Notethattheimplementationtraversesthegraphinadepth-firstway.
Containerhierarchies
Anotherwaytoapproachthesameproblemistousecontainerhierarchies.Youcancreatecontainersandconnectthemtoaparentcontainer,makingthemchildcontainers.Notethatunlessyouprovideadifferentcatalogtothechildcontainer,itwouldn’tbeofmuchhelpasinstantiationwillcontinuetohappenontheparent.Hence,whatyoushoulddoiseitherfiltertheparentcatalogbasedonacriterionthatdividesthesetofpartsthatshouldbecreatedontheparentcontainerfromthosethatshouldbecreatedonthechild,oryoushouldspecifyacompletelynewcatalogthatexposeasetofpartsthatshouldbecreatedonthechildcontainer.Asthechildisexpectedtobeshortlived,partscreatedinitwillbereleasedanddisposedearlier.
AcommonapproachistohaveSharedpartscreatedontheparentcontainerandNonSharedonthechildcontainer.AsSharedpartsmaydependonexportssuppliedbyNonShared,thenthemaincatalogwillhavetocontainthewholesetofpartswhereasthechildcontainershouldhaveafilteredviewofthemaincatalogwithonlythenonsharedparts.
Formoreinformationonthistopicpleasecheck
Disposalordering
Disposalorderingisnotguaranteedinanyway.ThatmeansthatyoushouldnottrytouseanimportinyourDisposemethod.Forexample:[code][Export]
publicclassSomeService:IDisposable
{
[Import]
publicILoggerLogger{get;set;}
publicvoidDispose()
{
Logger.Info("Disposing");//mightthrowexception!
}
}
[/code]
UsingtheimportedloggerinstanceonyourdisposemethodimplementationmaybeaproblemastheimplementationoftheILoggercontractmayalsobedisposable,andassuchmayhavebeendisposedalready.
AddPart/RemovePart
Noteverypartiscreatedbythecontainer.Youcanalsoaddandremovepartsfromit.Thisprocesstriggerscompositionandmaystartcreatingpartstosatisfydependenciesofthepartaddedrecursively.WhenthepartaddedisremovedMEFissmartenoughtoreclaimtheresourcesanddisposethenonsharedpartsusedbythepartadded.Note:thatMEFwillnevertakeownershipofaninstancesuppliedbyyou,butitdoeshavetheownershipofpartitcreatestosatisfyyourinstance’simports.
[code]usingSystem;
usingSystem.ComponentModel.Composition;
usingSystem.ComponentModel.Composition.Hosting;
usingSystem.ComponentModel.Composition.Primitives;
classProgram
{
staticvoidMain(string[]args)
{
varcatalog=newAssemblyCatalog(typeof(Program).Assembly);
varcontainer=newCompositionContainer(catalog);
varroot=newRoot();
//addexternalpart
container.ComposeParts(root);
//...usethecomposedrootinstance
//removesexternalpart
batch=newCompositionBatch();
batch.RemovePart(root);
container.Compose(batch);
}
}
publicclassRoot
{
[Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
publicNonSharedDependencyDep{get;set;}
}
[Export,PartCreationPolicy(CreationPolicy.NonShared)]
publicclassNonSharedDependency:IDisposable
{
publicNonSharedDependency()
{
}
publicvoidDispose()
{
Console.WriteLine("Disposed");
}
}
[/code]
深入思考和继续阅读
通常.NET程序的内存泄露原因:
Staticreferences
Eventwithmissingunsubscription
Staticeventwithmissingunsubscription
Disposemethodnotinvoked
IncompleteDisposemethod
有关如何避免.NET程序的内存泄露,请仔细阅读MSDN这两篇文章,详细讲述了<如何检测.NET程序内存泄露>以及<如何写高性能的托管程序>Howtodetectandavoidmemoryandresourcesleaksin.NETapplications WritingHigh-PerformanceManagedApplications:APrimer
有关.NET的自动内存管理机制、GC机制,垃圾回收原理等深层次内容,请仔细阅读下面的内容:
买书《CLRViaC#(3rdEdition)》,里面有《MemoryManagement》这一章专门讲述了.NETCLR的自动内存管理和垃圾回收机制 CodeProject上的文章《 MemoryManagementMisconceptions》有助你深入理解Root,Generation0,1…
相关文章推荐
- MEF引起的内存泄露
- Silverlight内存泄露(五)MEF等Ioc框架引起内存泄露-PartCreationPolicy
- Silverlight内存泄露(六)MEF等Ioc框架引起内存泄露-ExportLifetimeContext
- MEF等Ioc框架引起内存泄露-PartCreationPolicy
- Android开发 Handler引起的内存泄露
- ss目前的设计有引起内存泄露而导致down机的隐患
- 【HBase从入门到精通系列】如何避免HBase写入过快引起的各种问题
- 【转】线程问题引起的内存泄露
- Handler 引起的内存泄露
- 优化JS引起UIWebview的内存泄露
- Android学习系列(32)--App调试内存泄露之Cursor篇
- Handler引起的内存泄露问题
- Handler 引起的内存泄露
- 用Crt系列函数分析windows程序的内存泄露
- 5. oracle学习入门系列之五 内存结构、数据库结构、进程
- MEF学习系列(6): 在Silverlight中应用MEF
- Silverlight - 绑定造成的内存泄露
- Android 中 Handler 引起的内存泄露
- [Silverlight入门系列]使用MVVM模式(5):异步Validation数据验证和INotifyDataErrorInfo接口
- 【并发】ThreadLocal可能引起的内存泄露