[自带避雷针]DropShadowEffect导致内存暴涨

简介: 原文:[自带避雷针]DropShadowEffect导致内存暴涨                          [自带避雷针]DropShadowEffect导致内存暴涨                                                  周银辉  从学习WP...
原文: [自带避雷针]DropShadowEffect导致内存暴涨

                          [自带避雷针]DropShadowEffect导致内存暴涨

                                                 周银辉 

从学习WPF开始, 就知道"位图效果"不是什么省油的灯, 但我只知道它会占用很多cpu时间, 而显得效率低下, 但完全不料的是在某种特殊的硬件环境下其会导致内存暴涨.

事情是这样的, 我写了一个小程序, 看起来还不错, 但并不变态的日本朋友在变态的硬件环境下测试我的程序说"操作一段时间后程序会崩溃". 我远程VNC过去,发现程序每进行一个小操作(甚至是点击一下鼠标)内存会增长好几M,  照这个速度, 一会准崩溃, NND

但, 郁闷的事情是, 我无法重现这个Bug, 在开发团队和测试团队的机器上都没法重现(但在日本那边,这个bug是百分百重现)...内存泄露还没法重现, 好搞笑...更搞笑的事情在后面

最后我发现日本那边采用了如下的显示器配置: 其主机上连接了3个显示器, 第一个为普通屏, 第二和第三个屏是医学专用的高清屏(传说分辨率超高,当然价格也超贵), 当连接上这两个屏幕的时候, 我的程序就死翘翘了. 首先排除单纯的多屏问题,因为我的开发机就是双屏的, 然后我又排除了高清屏的问题, 因为禁用掉普通屏,而使用高清屏的时候,一切OK.

经过一系列的排查, 得到的诡异结论是: 当普通屏和高清屏同时启用, 并且我的程序窗口显示在普通屏上的时候, 内存暴涨, 最后死翘翘, 其他情况一切OK...

为什么要在这么诡异的环境下才能重现这个问题呢, 完全无解.

好吧, 我暂且承认是自己代码烂, 内存泄露了吧, 还好我有  .net memory profile ANTS Memory Profiler 

经过一番折腾之后, 这两个被我奉为"神器"的内存工具并没有给我任何答案: 从变化曲线上看内存是增长了许多(动不动就上百兆), 但内存快照中各个类型对象的内存占有率以及变化率都非常地正常, 晕, 丢失的内存哪里去了~~

我开始怀疑是P/Invoke之类的丢失了内存, 因为程序中有大量的win32 API平台调用, 然后,我注释掉了这些代码... 很不幸, 答案是NO.

没辙, 再来一招: 功能裁剪,这是经常使用的一招, 这让我们比较容易地缩小代码范围. 由于UI层和后台代码的耦合度非常低, 直接注释掉UI层上的XAML代码, 功能就能被很好地裁掉, 而不用更改逻辑代码, 所以功能裁剪显得比较容易. 我的程序都快被裁成空壳了, 问题依然存在... 
还有一个很搞怪的问题是, 比如我们定位到一个比较复杂的函数A会导致问题, 然后从A开始跟踪, 到B -> C -> D 绕了很长一圈后, 会到达一个及其简单的代码上比如 : this.width = myWidth; 注释到这个简单的代码就没问题, 否则导致问题, 诶, 难道是宽度的改变会引发什么事件,或者重写了控件重绘等等 而导致的问题? 这一点点希望很快就被抹杀了, 控件已经被我们抽取到足够的简单, 几乎没有什么代码.... 耍我么... 

一般, 在我绝望的时候, 我会使用我的大绝招: 我猜 

我一直比较相信写程序还是要靠灵感的(当然, 建立在你基础知识比较扎实的基础之上的). 那么我先猜是那个控件导致的问题呢, 我当然不会怀疑自己的代码咯, 我怀疑微软的代码, 这不是自大, 而是经验值, 我搞了不少无厘头的问题, 最后根源都在微软的代码上, 如果你有机会玩玩微软的RichTextBox控件再加上一个日文的Atok输入法, 你就会相信我说的话的(会搞死人的). 所以我猜ViewBox, 程序中的一个绘图面板放在ViewBox中(为实现缩放功能), 那好, 把绘图板从面板中剪切出来吧.

哈哈, OK啦,  把绘图板从面板中剪切出来后真的没问题也, 难道我运气这么好, 一下就猜中了...

为了验证, 我重新做了一个很简单的关于viewbox的DEMO, 放在日本那边的机器上, 没问题, 我晕

然后我注意到源程序中, viewbox放在一个ScrollViewer中, 并且这个ScrollViewer有自己的模板,  もしかしてなの ...

把画图板重新放回到ViewBox中, 在scrollViewer中动刀. 我发现, 我惊奇地发现:
 

在该ScrollViewer控件模板中, 居然有一个DropShadowEffect, 天杀的. 另外, 这个effect在我们的程序里面看不到阴影效果, 我们的程序也不需要这个效果, 所以一直没察觉.

删掉! 整个世界都安静了~

为了证明这个的确会导致问题, 所以我做了一个简单的DEMO,  

< Window  x:Class ="DropShadowEffectMomeryLeakDemo.Window1"
    xmlns
=""
    xmlns:x
=""
    Title
="Window1"  Height ="300"  Width ="300" >
    
< Canvas  x:Name ="canvas" >
        
< Canvas.Effect >
            
< DropShadowEffect />
        
</ Canvas.Effect >
        
< Button  Width ="100"  Height ="30"  Content ="Click me"  Click ="Button_Click" />
        
        

        
< Rectangle  Fill ="White"  Stroke ="Black"  Width ="53"  Height ="43"  Canvas.Left ="37"  Canvas.Top ="71" />
        
< Rectangle  Fill ="#FFAF8C8C"  Stroke ="Black"  Width ="54"  Height ="72"  Canvas.Left ="158"  Canvas.Top ="42" />
        
< Rectangle  Fill ="White"  Stroke ="Black"  Width ="54"  Height ="47"  Canvas.Left ="117"  Canvas.Top ="130" />
        
< Rectangle  Fill ="#FF21CB63"  Stroke ="Black"  Width ="44"  Height ="65"  Canvas.Left ="212"  Canvas.Top ="130" />
        
< Rectangle  Fill ="White"  Stroke ="Black"  Width ="42"  Height ="49"  Canvas.Left ="58"  Canvas.Top ="177" />
        
< Rectangle  Fill ="White"  Stroke ="Black"  Width ="53"  Height ="45"  Canvas.Left ="171"  Canvas.Top ="195" />
        
< Rectangle  Fill ="#FFDE13EE"  Stroke ="Black"  Width ="70"  Height ="69"  Canvas.Left ="224"  Canvas.Top ="30" />
        
< Rectangle  Fill ="White"  Stroke ="Black"  Width ="21"  Height ="43"  Canvas.Left ="100"  Canvas.Top ="42" />
        
< Rectangle  Fill ="White"  Stroke ="Black"  Width ="21"  Height ="22"  Canvas.Left ="171"  Canvas.Top ="8" />
        
< Rectangle  Fill ="White"  Stroke ="Black"  Width ="37"  Height ="43"  Canvas.Left ="117"  Canvas.Top ="71" />
        
< Rectangle  Fill ="White"  Stroke ="Black"  Width ="41"  Height ="33"  Canvas.Left ="49"  Canvas.Top ="130" />
        
< Rectangle  Fill ="#FFFF2B2B"  Stroke ="Black"  Width ="54"  Height ="31"  Canvas.Left ="100"  Canvas.Top ="195" />
    
</ Canvas >
</ Window >

 然后点击Button的时候,做如下操作:

         private   void  Button_Click( object  sender, RoutedEventArgs e)
        {
            
for ( int  i = 0 ; i < canvas.Children.Count; i ++ )
            {
                var c 
=  (FrameworkElement) canvas.Children[i];
                c.Width 
+=   1 ;
                c.Height 
+=   1 ;
            }
        }

 

在我的开发机上(在你的机器上可能也是), 连续点击button, 内存会有所增长, 但增长比较慢, 并且过一小会就释放带了, 所以内存基本维持到十多兆(图中18756K):
 

放在日本那个特殊的环境下,情况就完全不一样了(下图99340K, 并且好玩的是, 下图有黄色桌面背景的是第一个显示器, 蓝色背景的是第二个显示器, 把window1拖到第二个显示器上内存表现会很正常哦) :
 

 恩, 人生在于折腾

 

ps: 这台机器插了两张显卡: nVIDIA Quadro FX370 和 VREngine SMD5

 

 

 

目录
相关文章
|
SQL JSON Java
【Elasticsearch专栏 10】深入探索:Elasticsearch如何进行数据导入和导出
在Elasticsearch中,数据导入常通过Bulk API、Logstash或Java客户端进行,支持JSON、CSV等格式。导出则可通过SQL查询、Scroll API或第三方工具如elasticdump实现,将数据以JSON、CSV等格式导出。这些方法确保了数据的高效、安全导入与导出。
1363 5
|
监控 Windows
如何追踪Windows 进程自动异常退出
教你如何追踪Windows 进程自动异常退出
如何追踪Windows 进程自动异常退出
|
5月前
|
JavaScript 前端开发 程序员
甚至用不了五分钟就能学会vscode插件开发
本文介绍了VSCode插件的开发流程,从创建项目到最终发布。首先通过安装`yo`和`generator-code`脚手架工具初始化项目,选择JavaScript语言配置基础信息。接着,在`extension.js`中实现业务逻辑,例如将中文“变量”替换为“var”。通过F5进入调试模式验证功能。完成后使用`vsce`工具进行打包,解决可能遇到的版本不兼容或README文档问题。最后生成`.vsix`文件,可通过VSCode的“从VSIX安装”加载插件,实现开发闭环。进一步可将插件发布至官方市场供更多开发者使用。
|
10月前
|
存储 关系型数据库 MySQL
MySQL MVCC全面解读:掌握并发控制的核心机制
【10月更文挑战第15天】 在数据库管理系统中,MySQL的InnoDB存储引擎采用了一种称为MVCC(Multi-Version Concurrency Control,多版本并发控制)的技术来处理事务的并发访问。MVCC不仅提高了数据库的并发性能,还保证了事务的隔离性。本文将深入探讨MySQL中的MVCC机制,为你在面试中遇到的相关问题提供全面的解答。
795 2
|
10月前
|
前端开发 JavaScript API
取网页纯文本内容免费API接口教程
该API用于获取指定网页的纯文本内容,去除HTML标签、CSS和JS等元素。支持POST和GET请求,需提供ID、Key、URL等参数。请求示例:https://cn.apihz.cn/api/wangzhan/getyuan.php?id=88888888&key=88888888&url=www.apihz.cn&dy=1。返回纯文本数据。
|
开发者 C# Windows
WPF布局大揭秘:掌握布局技巧,轻松创建响应式用户界面,让你的应用程序更上一层楼!
【8月更文挑战第31天】在现代软件开发中,响应式用户界面至关重要。WPF(Windows Presentation Foundation)作为.NET框架的一部分,提供了丰富的布局控件和机制,便于创建可自动调整的UI。本文介绍WPF布局的基础概念与实现方法,包括`StackPanel`、`DockPanel`、`Grid`等控件的使用,并通过示例代码展示如何构建响应式布局。了解这些技巧有助于开发者优化用户体验,适应不同设备和屏幕尺寸。
458 0
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的高校后勤报修系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的高校后勤报修系统的详细设计和实现(源码+lw+部署文档+讲解等)
202 1
|
Java 程序员 数据处理
探索编程范式:面向对象编程与函数式编程的比较与取舍
本文将探讨面向对象编程(OOP)和函数式编程(FP)两种主流的编程范式,并比较它们在代码组织、可复用性、并发性和代码风格等方面的特点。通过深入了解它们的优势和不足,读者可以更好地选择适合自己项目需求的编程范式。
418 1
|
运维 数据可视化 物联网
快速开发光伏电站数字孪生运维系统
在开发光伏电站数字孪生系统过程中,涉及物联网、孪生模型构建、实时数据计算、数据智能、3D模型渲染及数据联动等多项复杂工作,IoT孪生引擎帮助开发者快速构建出符合自身业务特性的数字孪生系统。
|
并行计算 数据处理 开发者
编程范式的抉择:面向对象编程与函数式编程的对决
在当今的软件开发领域,面向对象编程(Object-Oriented Programming,OOP)和函数式编程(Functional Programming,FP)是两种重要的编程范式。本文将比较并探讨这两种编程范式的特点、优势和适用场景,以帮助开发者在编程选择上做出明智的决策。

热门文章

最新文章