RSS 2.0 Feed
VS.NET
Visual Studio .NET
摘要:抓到一个mini dump,执行!clrstack,会提示如下错误: *** WARNING: Unable to verify timestamp for mscorwks.dll Failed to load data access DLL, 0x80004005 Verify that 1) you have a recent build of the debugger (6.2.14 or newer)             2) the file mscordacwks.dll that matches your version of mscorwks.dll is                 in the version directory             3) or, if you are debugging a dump file, verify that the file                 mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.             4) you are debugging on the same architecture as the dump file.                 For example, an IA64 dump file must be debugged on an IA64                 machine. You can also run the debugger command .cordll to control the debugger's load......[阅读全文]

posted @ | Feedback (0) | Filed Under [ VS.NET ]

摘要:对于XmlSerializer带来的内存占用过高,最终导致Out Of Memory的问题,参见以前这个链接:http://www.cnblogs.com/juqiang/archive/2008/01/15/1039936.html (但是那篇文章中对于XmlSerializer构造方法的说明,是错误的。那段代码没有问题,有问题的是下面的) 首先看System.Xml.Serialization.XmlSerializer的构造方法,一共分为三大类: public XmlSerializer(Type type) : this(type, (string) null) public XmlSerializer(Type type, string defaultNamespace) 这两个方法,采用了上文引用的那篇文章的处理方式,应用了cache,这是正确的,不会造成内存占用过高。(上文引用那篇文章里面这个地方解释错了) 另一大类的方法是: public XmlSerializer(XmlTypeMapping xmlTypeMapping),这个方法里面会产生一个tempAssembly,但是没有用缓存方式来处理。 有问题的一类方法是: public XmlSerializer(Type type, Type[] extraTypes) : this(type, null, extraTypes, null, null, null, null) public XmlSerializer(Type type, XmlAttributeOverrides overrides) : this(type, overrides, new Type[0], null, null, null, null) public XmlSerializer(Type type, XmlRootAttribute root) : this(type, null, new Type[0], root, null, null, null) public XmlSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, string defaultNamespace) : this(type, overrides, extraTypes, root, defaultNamespace, null, null) public XmlSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, string defaultNamespace, string location, Evidence evidence) 前4个都最终调用了最后一个构造方法,里面产生了一个tempAssembly,也没有用缓存方式来护理。 我上次提到的那个问题,问题代码如下: public static TReturn Convert<TReturn, TInput>(TInput input) where TReturn: class, new() where TInput: IProvisioningObject {     using (MemoryStream stream......[阅读全文]

posted @ | Feedback (1) | Filed Under [ VS.NET ]

摘要:    网站升级后,说有High CPU的问题。mstsc上去后,看了一下,果然如此,w3wp.exe的cpu几乎一直是满的,我的远程桌面操作也很慢。下载下来windbg,装好,抓了两个dump。首先!runaway一下, 0:022> !runaway  User Mode Time   Thread       Time   22:8cc       0 days 0:17:15.238   23:4d8       0 days 0:15:20.936   15:898       0 days 0:09:15.316   24:c64       0 days 0:05:07.587 这是前4个线程,一共占用了47分钟左右,我们再看一下time的时间: 0:022> .time Debug session time: Sun Mar  9 15:07:52.000 2008 (GMT+8) System Uptime: 1 days 2:04:59.903 Process Uptime: 0 days 0:55:38.000   Kernel time: 0 days 0:00:07.000   User time: 0 days 0:48:41.000 cool,.time的时间和!runaway的几乎一致,再看看!threadpool的数字: 0:022> !threadpool CPU utilization 100% Worker Thread: Total: 5 Running: 5 Idle: 0 MaxLimit: 200 MinLimit: 2 Work Request in Queue: 15 Unknown Function: 6a2aa6fb  Context: 0190c5b8 AsyncTimerCallbackCompletion TimerInfo@01969b30 AsyncTimerCallbackCompletion TimerInfo@01969428 Unknown Function: 6a2aa6fb  Context: 0190ccb8 AsyncTimerCallbackCompletion TimerInfo@019698a8 AsyncTimerCallbackCompletion TimerInfo@01969c98 AsyncTimerCallbackCompletion TimerInfo@019f5778 AsyncTimerCallbackCompletion TimerInfo@01918658 AsyncTimerCallbackCompletion TimerInfo@01969788 Unknown Function: 6a2aa6fb  Context: 0190cb38 AsyncTimerCallbackCompletion TimerInfo@019695d8 Unknown Function: 6a2aa6fb  Context: 0190caf8 AsyncTimerCallbackCompletion TimerInfo@01969740 Unknown Function: 6a2aa6fb  Context: 0190c178 Unknown Function: 6a2aa6fb ......[阅读全文]

posted @ | Feedback (4) | Filed Under [ VS.NET ]

摘要: Paint事件被触发了多少次?比较简单的方式,我们自己做一个perfmon能用的counter。看代码:  1        private void button1_Click(object sender, EventArgs e) 2        { 3             4            if (!PerformanceCounterCategory.Exists("GDI+ Monitor")) 5            { 6                CounterCreationDataCollection ccdc = new CounterCreationDataCollection(); 7 8                CounterCreationData totalPaint = new CounterCreationData(); 9                totalPaint.CounterName = "# operator executed";10                totalPaint.CounterHelp = "Counts of OnPaint events called";11                totalPaint.CounterType = PerformanceCounterType.NumberOfItems32;1213                ccdc.Add(totalPaint);1415                PerformanceCounterCategory.Create("GDI+ Monitor", "Some counters for GDI+ objects", PerformanceCounterCategoryType.MultiInstance, ccdc);16            }17        }好,点一下button1之后,我们就可以在perfmon中看到GDI+ Monitor这个category了,然后只有一个counter,就是# operator executed然后在winform中增加这么一段:            paintcall = new PerformanceCounter();            paintcall.CategoryName = "GDI+ Monitor";            paintcall.CounterName = "# operator executed";            paintcall.MachineName = ".";            paintcall.ReadOnly = false;好了,代码里面可以用paintcall这个变量了。我们现在的需求是监视Paint被触发了多少次,那么可以在代码中这么写:  1            Graphics g2 = e.Graphics; 2 3            Bitmap bmp = new Bitmap(this.Width, this.Height); 4            Graphics g = Graphics.FromImage(bmp); 5 6            paintcall.Increment(); 7 8            Rectangle r = new Rectangle(0, 0, this.Width, this.Height); 9            g.FillRectangle(new LinearGradientBrush(r, Color.Red, Color.Blue, LinearGradientMode.BackwardDiagonal), r);1011            g2.DrawImage(bmp, new Point(0, 0));1213            g.Dispose();14            g = null;1516            bmp.Dispose();17            bmp = null;看上面第6行,这句会把paint call增加一。当然,其他方法有很多,这里不写了。好,跑一下perfmon,然后把我们新增加的counter add上,嗯,可以看到当窗口无效的时候,计数器就增加了。有一个小的细节,当窗口稍微改动大小的时候,你会发现Paint被调用了多次,这个实在很郁闷。so,稍微做点手脚:  1        private bool resize = false; 2        private void Form1_ResizeEnd(object sender, EventArgs e) 3        { 4            resize = false; 5            this.Invalidate(); 6        } 7 8        private void Form1_ResizeBegin(object sender, EventArgs e) 9        {10            resize = true;11        }然后我们修改一下Paint事件的代码如下:  1        private void Form1_Paint(object sender, PaintEventArgs e) 2        { 3            if (true == resize) return; 4 5            Graphics g2 = e.Graphics; 6 7            Bitmap bmp = new Bitmap(this.Width, this.Height); 8            Graphics g = Graphics.FromImage(bmp); 910            paintcall.Increment();1112            Rectangle r = new Rectangle(0, 0, this.Width, this.Height);13            g.FillRectangle(new LinearGradientBrush(r, Color.Red, Color.Blue, LinearGradientMode.BackwardDiagonal), r);1415            g2.DrawImage(bmp, new Point(0, 0));1617            g.Dispose();18            g = null;1920            bmp.Dispose();21            bmp = null;22        }注意第三行,我们判断,如果在resize过程中,那么就直接返回。这样,虽然界面像白板一样,但是却少了很多Paint操作,从性能上会好不少。 ...[阅读全文]

posted @ | Feedback (4) | Filed Under [ VS.NET ]

摘要: GDI+自身是否有leak,我们不去管,现在说的是.NET代码中的处理。首先看我这个简单的helper using System;using System.Diagnostics;using System.Text;using System.Runtime.InteropServices;public class MemoryReport{    [DllImport("user32.dll", CharSet=CharSet.Auto)]    public static extern long GetGuiResources(IntPtr hProcess, long flag);    public static string Write(){        Process p = Process.GetCurrentProcess();        ing hcount = p.HandleCount;        long psize = p.PrivateMemorySize64;        long vsize = p.VirtualMemorySize64;        long workset = p.WorkingSet64;        long gcsize = GC.GetTotalMemory(false);        int gdiobjs = (int)(GetGdiResources(p.Handle,0));        int userobjs = (int)(GetGdiResources(p.Handle,1));        return String.Format("Handle count:{0:N0},Private Bytes:{1:N0}K, Virtual Bytes:{2:N0}K, Working Set:{3:N0}K, GC Heap Size:{4:N0}K, GDI Objects:{5:N0}, User Objects:{6:N0}", hcount, psize>>10, vsize>>10, workset>>10, gcsize>>10, gdiobjs, userobjs);}            }现在我们做一个winform程序,放一个button,在click里面写如下测试代码: for(int i=0;i<1000;i++){    Bitmap b = new Bitmap("c:\\1.bif");    IntPtr ip = b.GetHbitmap();    Bitmap b2 = Bitmap.FromHbitmap(ip);}MessageBox.Show(MemoryReport.Write());观察每次的结果,Private Bytes/ Virtual Bytes/ Working Set基本是一个上涨的走向。但是我们感兴趣的是这几个地方:1、Handle count:这个值一般会波动变化,在这里例子里面,你把程序运行起来后,用taskmgr来观察Handle Count一栏(默认的没有,需要你自己手工添加这个column),一般是100以下。然后点一下按钮,handle count会增长1000左右,再点几次,会在1000上下波动,不会继续增长。2、GDI Objects:这个值每次会增加10003、你连续点10次这个button,嘣!程序crash了。。。如果看dump里面的异常,会是什么bitmap的一个构造方法的parameter不正确。4、GC Heap Size很小很小,我这里是2M。但是virtual size很大。对于1,为什么这样,我不清楚;对于2,原因在于GetHbitmap返回的是一个Unmanged resource,GC不会回收(即使你使用了GC.Collect()这个值也不会下降的);对于3,OS默认的每个process的GDI objects上限为10000个,我们代码中是循环了1000次,所以如果你点了10次button,程序就会完蛋。对于4,说明leak的资源是unmanged resource,so,gc heap看起来很乖。那么,如何修复上面的问题2?既然是unmanged resource,我们就要从unmanged找起。 [DllImport("gdi32.dll", CharSet=CharSet.Auto)]public static extern IntPtr DeleteObject(IntPtr hobj);for(int i=0;i<1000;i++){    Bitmap b = new Bitmap("c:\\1.bif");    IntPtr ip = b.GetHbitmap();    Bitmap b2 = Bitmap.FromHbitmap(ip);     DeleteObject(ip);}MessageBox.Show(MemoryReport.Write());嗯,再运行一次,好了!GDI objects稳定了,再也没有变化过。不过,我们修改一下循环计数器,到5000吧,然后观察Handle count,波动的比较厉害,内存相关的三组数值也稍有变化。好,我们再修改一次程序 [DllImport("gdi32.dll", CharSet=CharSet.Auto)]public static extern IntPtr DeleteObject(IntPtr hobj);for(int i=0;i<1000;i++){    Bitmap b = new Bitmap("c:\\1.bif");    IntPtr ip = b.GetHbitmap();    Bitmap b2 = Bitmap.FromHbitmap(ip);     b.Dispose();     b2.Dispose();     DeleteObject(ip);}MessageBox.Show(MemoryReport.Write());重新run一次,嗯,这个世界终于清静了,handle count/gdi resource/ mem size都很平稳。so,总结一下,对于类似上面的、可能被反复调用的type,如GDI+ obj,可以考虑使用完毕后立刻Dispose,这样可以被GC提早回收。对于返回一个IntPtr的方法,要仔细看,是不是需要再call win32里面对应的Delete方法。对于绝大多数GDI+ obj,我们只需要DeleteObject即可,但是对于icon,我记着是另外一个函数,有兴趣的可以在msdn上查一下。...[阅读全文]

posted @ | Feedback (11) | Filed Under [ VS.NET ]

摘要:1、这本书对于初学者没有太大用处2、这本书对于眼中只有架构、自己不写程序的、鄙视代码的人没有用处3、这本书对于非微软的人用处不算太大,你不知道ms内部的数据结构,你没有private symbols。4、这本书对于微软的人用处不算太大,搞debug的就那么几号人 5、这本书对于在客户现场被骂的狗血喷头的、自己即使架了.NET IDE也不知道如何找出问题的人很有用处   如果你是第五种人,疯狂购买吧!...[阅读全文]

posted @ | Feedback (4) | Filed Under [ Solution VS.NET ]

摘要:此文是偶的偶像和哥们的,转过来,替他做一下宣传。偶会买10本,9本送人,有要的,现在报名。 (原来公司大量的COM+和.NET的case,都是熊老大做的)   Windows 高效排错《Windows 高效排错》 可以在CSDN读书频道预览了 地址在这里:http://book.csdn.net/bookfiles/555/ 读书频道的排版有些问题,看起来不是很舒服。如果想看PDF的,可以在这里下载 http://www.cnblogs.com/lixiong/archive/2006/08/16/475520.html 纸板书籍估计在11月中下旬面世  现在在China-pub, dearbook等网上书店已经有介绍,地址分别 http://www.china-pub.com/computers/common/info.asp?id=37008http://www.dearbook.com.cn/book/230727 您的看法非常重要。如果你看过这本书的PDF,了解本书的内容和潜在读者,请您在上面的链接中留下你的观点,便于其它不了解这本书的人选择。 如果您通过这本书介绍的方法解决过实际的问题,非常希望您能够分享您的经验。您可以发邮件到eparg@msn.com。如果您的分享能够帮助其它读者,我非常希望能够送书给您作为答谢。 如果您对书中所述内容有疑惑,也欢迎您写信来讨论。我会尽快回复。如果我们之间的讨论对其他读者有帮助,我也会放到网上,同时送书给您作为答谢。 === 上面是官腔了。我个人想说的话其实是: 1) 书前言里面说,"2007年年初我不再做技术支持。希望这本书能帮我记录下这一段美好的经历",其中最让我难忘的就是跟xiao,elan和leo站在水房门口讨论case的日子2) 常来我blog踩的同学们,你们要书的话把地址留给我。等我拿到了我给大家发。(估计要等一段时间。出版社就给我6本。剩下的我争取去批发)3) 跟我一起做过case的,如果你需要的话也把地址留给我4) 多卖一本书,我可以多拿3块钱不到。整本书的稿费还不够在上海买半个车牌,或者买个厕所。我一点也不关心字面上的销量。我之所以花那么多时间来做这个亏本生意,目的只有一个,就是希望跟喜欢调试的人分享其中的过程,让真正需要这方面文章的人有所参考。所以如果你身边有这本书的潜在读者,劳烦你推荐一下。...[阅读全文]

posted @