惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
Y
Y Combinator Blog
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 【当耐特】
S
SegmentFault 最新的问题
IT之家
IT之家
Recent Announcements
Recent Announcements
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
阮一峰的网络日志
阮一峰的网络日志
T
The Blog of Author Tim Ferriss
Martin Fowler
Martin Fowler
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
WordPress大学
WordPress大学
博客园 - Franky
L
LangChain Blog
人人都是产品经理
人人都是产品经理
小众软件
小众软件
博客园 - 叶小钗
罗磊的独立博客
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
雷峰网
雷峰网
腾讯CDC
Google DeepMind News
Google DeepMind News
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Help Net Security
Help Net Security
C
Check Point Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Schneier on Security
Schneier on Security
博客园 - 聂微东
A
Arctic Wolf
H
Heimdal Security Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Exploit Database - CXSecurity.com
C
Cyber Attacks, Cyber Crime and Cyber Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Google DeepMind News
Google DeepMind News

博客园 - 拖鞋不脱

【随】不好用的Ria Services 【原】检测是否包含特定字符串的几种方法以及性能比较 【原】StreamInsight 浅入浅出(六)—— Debugger 【原】StreamInsight 浅入浅出(五)—— LINQ 语法 【原】StreamInsight 浅入浅出(四)—— 例子 【原】StreamInsight 浅入浅出(三)—— 适配器 【原】StreamInsight 浅入浅出(二)—— 流与事件 【原】Silverlight 自定义控件模板管理 【原】StreamInsight 浅入浅出(一)—— 基本概念 【原】小软件开发心得(二)——推广、测试 【原】小软件开发心得(一)——需求、开发 【原】豆瓣电台桌面版插件开发——搜索插件 【原】豆瓣电台桌面版——Win7任务栏支持+托盘+快捷键+历史记录 【原】Sql Server性能优化——Slide Window 【原】Sql Server性能优化——Partition(管理分区) 【随】Silverlight请求服务的缓存文件问题 【原】Sql Server性能优化——Partition(创建分区) 【原】Sql Server性能优化——Compression 【译】VS2010中C#的增强
【原】Silverlight内存泄漏原因、检测及解决(Memory Leak of Silverlight:Reason、Detection and Solving)
拖鞋不脱 · 2013-03-10 · via 博客园 - 拖鞋不脱

2013-03-10 17:29  拖鞋不脱  阅读(2448)  评论()    收藏  举报

在.Net 中,内存的管理和释放都由GC(Garbage Collection)管控,一般不需太多关注。但依然可能有内存泄漏(隐式内存泄漏)的情况出现,即一些早应结束它的生命周期的对象,直到程序结束才会被释放。具体到Silverlight中的表现,就是Silverlight所在浏览器进程占用的内存不断增长,直到崩溃或关闭浏览器。

In .Net world, GC manages the memory. So we normally don’t need too much concern over the memory mangement. But there still can be memory leak issue: some instances dont’t release memory until the end of application, while they have ended their life circle long before. Specific to the Silverlight situation, is the memory of the browser process who takes the silverlight continues to grow, until the collapsing or the closing of the browser.

内存泄漏产生的原因 (Reason of the Memory Leak)

对象是否释放是由GC管理的,什么样的对象会被GC释放?已经不再被应用程序的Root或者别的对象所引用的对象。而内存泄漏,就出现在那些从业务角度来看,应该已经不被引用,而实际仍被其他对象所引用的对象上。

假设有A、B两个对象,A引用了B,那么当A没有释放的时候,B也就不会被释放。如果A又恰巧是程序的Root或被Root所引用(如全局静态变量),那么B就很不幸的在整个程序的生命周期中都不会被释放。

这种情况一般不会发生,因为如果某个对象和Root产生了关联,往往是我们有意识的将它塑造成一个全局的、希望在整个程序的生命周期都存在的对象,这就不存在内存泄漏的问题。而意外往往就发生在我们无意识的地方——事件。

假设A有事件EventA,我们在B中处理了A的EventA,往往会写成这样:A.EventA += B.HandleEventA; 这时也许你不会意识到我们已经在A中添加了B的引用,因为在A中我们需要把B添加到它的监听者集合中,以便每次触发EventA的时候,都调用B的HandleEventA来处理这个事件。如果我们事后没有及时的通过A.EventA –= B.HandleEventA; 来将B从A的监听者集合中移除,那么B就会成为导致内存泄漏的一个元凶。

所以,全局事件监听、静态变量的事件监听,都可能在无意间消耗了我们大量的内存。

内存泄漏的检测 (Detection of the Memory Leak)

看到上面一节,也许你已经意识到在自己的应用中有这样的问题并准备着手解决。不过我依然想先提供两种检测内存泄漏的简单方案:

  1. 内存监控
    内存泄漏的表象就是应用占用的内存在不断增长而从不减少,这说明没有或很少有对象被释放了。如果你的Silverlight应用程序在每一次页面切换时都会增长内存占用,那么你的程序很可能就存在内存泄漏的问题。我们可以用任务管理器、Process Explorer或者VMMap等工具来监控Silverlight所在浏览器进程的内存增长情况,尤其是Private部分。
  2. 析构函数跟踪
    GC在释放对象时,总会调用它们的析构函数(Finalize方法),虽然一般不需要实现类的析构函数,但我们依然可以利用它来监控对象是否被释放。我们可以在析构函数中添加跟踪锚点(在Debug时添加断点,或者输出日志),来看看对象的析构函数是否被释放。如果断点始终没有命中或者日志从来什么也没有输出,那么恭喜你,你发现了内存泄漏的问题。

以上两种方法可以用来判断是否存在内存泄漏的问题,简单易行,而且结合对内存泄漏原因的理解,如果代码结构良好,逻辑清晰的话,应该很容易找到是哪里出现了不恰当的引用,从而解决内存泄漏的问题。但如果逻辑本身比较复杂,引用交织纷繁的话,我推荐利用Windbg进行更详细系统的排查,具体内容参见:http://blogs.msdn.com/b/delay/archive/2009/03/11/where-s-your-leak-at-using-windbg-sos-and-gcroot-to-diagnose-a-net-memory-leak.aspx

内存泄漏问题的解决 (Solving of the Memory Leak)

如同内存泄漏问题的检测有多种手段,内存泄漏问题的解决依然有多种方式:

  1. 直接移除事件监听。如上文所述,通过 A.EventA –= B.HandleEventA,直接移除了事。这种方式快捷明了,适用于有明确的生命起始、截止点的对象。如一个页面,我们可以把所有事件的监听(+=)放在页面的Loaded事件中,并在页面的UnLoaded事件中将事件监听移除,因为页面Unloaded说明页面已被移除并不会再启用。一些View甚至控件也可以用这种方式处理,只要我们确定,在UnLoaded之后,它们在业务上已经完成使命。
  2. 使用Weak Event Pattern(弱事件模式)。虽然直接移除比较干脆可控,但有些时候我们并无法确定一个对象真正的生命终结点。比如一个控件,当我们把它从当前View移除的时候就会触发它的UnLoaded事件,但我们不可以妄下结论说它就是生命终结了,也许使用者需要它在不可见的地方继续处理一些逻辑。而非View层的控制类,更无法在其内部判断它的起始、终结。我们可以通过让它们应用IDisposable接口,在Dispose方法中移除事件监听,并强制要求外部调用时,在合适的时候调用它的Dispose方法。但如果真的这么做,你会发现这可能会带来更多的问题——代码更复杂,耦合性更高,潜在问题更为隐蔽。
    所以微软推荐使用弱事件模式(Weak Event Patterns):http://msdn.microsoft.com/en-us/library/aa970850.aspx。原理就是将事件处理的B和触发事件的A之间的关联解除,使用中间件SomeEventWeakEventManager来调度,从而使A不会再直接引用B。
  3. WeakEventListener。事实上WeakEventListener就是Weak Event Pattern的一种实现,它利用了.Net提供的WeakReferrence类来很好的实现了引用管理。具体详见http://blogs.msdn.com/b/delay/archive/2009/03/09/controls-are-like-diapers-you-don-t-want-a-leaky-one-implementing-the-weakevent-pattern-on-silverlight-with-the-weakeventlistener-class.aspx,有说明有示例还有代码。值得一提的是,文下的评论(RScullard)中还提到了一种错误的用法,在做事件关联时,一定要保证OnEventAction和OnDetachAction是静态的,不要和当前上下文产生关联而引入额外的引用关系。

总结 (Summary)

所以我们要注意在Silverlight乃至.Net开发中的内存泄漏问题,尤其是全局事件或静态变量事件的处理,通过内存监控、析构函数跟踪等方法确定内存泄漏的发生,并通过Weak Event Pattern来规避此类问题的发生。

扩展阅读:http://blogs.msdn.com/b/delay/archive/2010/02/25/highlighting-a-weak-contribution-enhancements-make-preventing-memory-leaks-with-weakeventlistener-even-easier.aspx