微软急聘base在上海的MOSS高手!

 

Premier Field Engineer – SharePoint/MOSS

 

The purpose of the Premier Field Engineer (PFE) position is to provide Microsoft customers with reliable technical solutions to the complex integration problems associated with business solutions built using Windows SharePoint Services, SharePoint Portal Server and Microsoft Office SharePoint Server. Typical tasks performed in this role include specific problem isolation and correction, conducting application design and technical reviews, performance tuning, application stability consulting/troubleshooting, code reviews, porting/migration assistance, configuration/administration management, pre-rollout testing and general development consulting. The prospective PFE candidate should draw upon all resources at Microsoft, to advise and consult on the use of Microsoft SharePoint Technologies to avoid such problems in the future.

Requirements:

• Candidates must have exceptional customer service, problem solving, communication skills, and the ability to work in a team environment.
• Must have sufficient technical depth to communicate with development and other internal organizations at a peer level.
• Must possess the ability to work with minimal supervision and operate as a self contained business unit within the PFE team.
• Must demonstrate the aptitude for providing exceptional customer service in politically charged environments.
• Show the ability to enhance the technical expertise of peers via training development and delivery, mentoring of new hires, and team content development.
• Demonstrate strategic thinking with value-add contributions.
• Strong business background in Fortune 500 and/or experience with systems technology consulting firm desired.

Technical Requirements:
• Strong knowledge & technical proficiency with Microsoft SharePoint Technologies including Windows SharePoint Services, SharePoint Portal Server, & Microsoft Office SharePoint Server.
• Solid understanding of the Microsoft Windows platform and standard client/server, networking, and Internet fundamentals.
• Superior problem solving and troubleshooting skills at the System Engineer level.
• From this foundation, the PFE position is exposed to many technologies, including but not limited to: IIS, SQL Server, development with .NET languages, XML and Scripting technologies, and data access technologies.
• Practical user mode debugging is a preferred skill, but not required.

Travel requirements vary regionally. Engineers must be available for travel dispatch 24x7x365.

 
Education:
College degree, preferably in Computer Science, is required. MCTS in SharePoint Technologies or other applicable advanced certification is strongly preferred, but not required at hire; however, must be obtained within 12 months of hire. We will consider related field (or equivalent) experience.

Microsoft is an equal opportunity employer (EOE) and strongly supports diversity in the workforce.

 有意向的请和我联系:

发表在 未分类 | 留下评论

Unable to load image C:WINDOWSMicrosoft.NETFrameworkv2.0.50727mscorwks.dll, Win32 error 0n2

抓到一个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 of mscordacwks.dll.  .cordll -ve -u -l will do a verbose reload.
If that succeeds, the SOS command should work on retry.

If you are debugging a minidump, you need to make sure that your executable
path is pointing to mscorwks.dll as well.

解决办法很简单,把抓到的dump那个机器上的mscorwks拷过来,如在:c:dumpsrulesdebug目录下,那么执行:
0:037> .exepath+ c:dumpsrulesdebug
Executable image search path is: C:WINDOWSMicrosoft.NETFrameworkv2.0.50727;c:dumpsrulesdebug

然后重新加载符号表即可:.reload

发表在 未分类 | 标签为 | 留下评论

XmlSerializer带来的性能问题及解决办法

对于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 = new MemoryStream())
    {
        new XmlSerializer(typeof(TInput)).Serialize((Stream) stream, input);
        stream.Position = 0L;
        XmlSerializer serializer = new XmlSerializer(typeof(TReturn), new XmlRootAttribute(input.GetType().Name));
        return (TReturn) serializer.Deserialize(stream);
    }
}
注意红色的代码,这里产生了一个tempAssembly,没有做缓存。这里的核心问题在于,tempAssembly不会被自动释放掉,除非appdomain被unload。
修正的方式类似于XmlSerializer的处理方式,采用一个二元Hashtable来做。代码修改如下:

 1 private static TempXmlSerializerCache cache = new TempXmlSerializerCache();
 2
 3        public static TReturn Convert<TReturn, TInput>(TInput input)
 4            where TReturn : classnew()
 5            where TInput : IProvisioningObject
 6        {
 7            using (MemoryStream stream = new MemoryStream())
 8            {
 9                new XmlSerializer(typeof(TInput)).Serialize((Stream)stream, input);
10                stream.Position = 0L;
11
12                XmlSerializer serializer = cache[typeof(TReturn).ToString(), input.GetType().Name];
13                if (serializer == null)
14                {
15                    lock (cache)
16                    {
17                        serializer = cache[typeof(TReturn).ToString(), input.GetType().Name];
18                        if (serializer == null)
19                        {
20                            serializer = new XmlSerializer(typeof(TReturn), new XmlRootAttribute(input.GetType().Name));
21                            cache.Add(typeof(TReturn).ToString(), input.GetType().Name, serializer);
22                        }

23                    }

24                }

25                return (TReturn)serializer.Deserialize(stream);
26            }

27        }

这里的二元Hashtable,是从XmlSerializer里面扒出来的,稍微修改了一下而已:

    public class TempXmlSerializerCache
    
{
        
private Hashtable cache = new Hashtable();

        
public void Add(string ns, object o, XmlSerializer serializer)
        
{
            XmlSerializerCacheKey key 
= new XmlSerializerCacheKey(ns, o);
            
lock (this)
            
{
                
if (this.cache[key] != serializer)
                
{
                    Hashtable hashtable 
= new Hashtable();
                    
foreach (object obj2 in this.cache.Keys)
                    
{
                        hashtable.Add(obj2, 
this.cache[obj2]);
                    }

                    
this.cache = hashtable;
                    
this.cache[key] = serializer;
                }

            }

        }


        
public XmlSerializer this[string ns, object o]
        
{
            
get
            
{
                
return (XmlSerializer)this.cache[new XmlSerializerCacheKey(ns, o)];
            }

        }

    }

以及:

   public class XmlSerializerCacheKey
    
{
        
private string ns;
        
private object type;

        
public XmlSerializerCacheKey(string ns, object type)
        
{
            
this.type = type;
            
this.ns = ns;
        }


        
public override bool Equals(object o)
        
{
            XmlSerializerCacheKey key 
= o as XmlSerializerCacheKey;
            
if (key == null)
            
{
                
return false;
            }

            
return ((key.type == this.type) && (key.ns == this.ns));
        }


        
public override int GetHashCode()
        
{
            
return (((this.ns != null? this.ns.GetHashCode() : 0^ ((this.type != null? this.type.GetHashCode() : 0));
        }

    }

发表在 未分类 | 标签为 | 一条评论

网站High CPU分析

    网站升级后,说有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  Context: 0190cbf8
————————————–
Number of Timers: 9
————————————–
Completion Port Thread:Total: 2 Free: 2 MaxFree: 4 CurrentLimit: 2 MaxLimit: 200 MinLimit: 2

这些信息基本够了,罪魁祸首就是!runaway跑的那几个线程,我们随便抓两个来看看:

0:022> kb
ChildEBP RetAddr  Args to Child             
00005205 7a4c72c0 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.RegexCharClass.CharInClassRecursive(Char, System.String, Int32)+0x20
00005205 7a4b16e5 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.RegexInterpreter.Go()+0xc40
00005205 7a4b15c3 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.RegexRunner.Scan(System.Text.RegularExpressions.Regex, System.String, Int32, Int32, Int32, Int32, Boolean)+0xa5
00005205 7a4b14af 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.Regex.Run(Boolean, Int32, System.String, Int32, Int32, Int32)+0x103
ffffffff 7a4dc313 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.Regex.Match(System.String, Int32)+0x1f
ffffffff 7a4d1c39 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.RegexReplacement.Replace(System.Text.RegularExpressions.Regex, System.String, Int32, Int32)+0x47
0606f8ac 7a4d1b53 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.Regex.Replace(System.String, System.String, Int32, Int32)+0x99
05d8ed04 0ff965c7 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.Regex.Replace(System.String, System.String)+0x2f
05d8ed04 0ff96530 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Text.HtmlHelper.RemoveHtmlComments(System.String)+0x67
00000000 0ff96477 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Text.HtmlHelper.RemoveHtmlAndComments(System.String)+0x38
10c3e8a8 0ff96142 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Components.AggregatedPost.set_Description(System.String)+0x2f
10c3e8a8 0ff95d8e 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Data.DataHelper.LoadAggreatedPost(System.Data.IDataReader)+0x1e2
10c3e910 0ff95c02 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Data.DatabaseObjectProvider.GetAggregatedPosts(Int32, Int32, Int32)+0x126
10c3e958 0ff9e4eb 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Data.Cacher.GetPagedAggregatedPosts(Int32, Int32, Int32, 3rdparty.Framework.CacheDuration, Boolean)+0x14a
10c3e9b0 0ff9e364 00000000 00000000 00000000 3rdparty_Blog_Web!3rdparty.Blog.Web.WebForm1.BindData()+0x16b
10c3ec04 66f12980 00000000 00000000 00000000 3rdparty_Blog_Web!3rdparty.Blog.Web.WebForm1.Page_Load(System.Object, System.EventArgs)+0x44
10c3ec04 6628eff2 00000000 00000000 00000000 System_Web_RegularExpressions_ni!System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)+0x10
10c3ec04 6613cb24 00000000 00000000 00000000 System_Web_ni!System.Web.Util.CalliEventHandlerDelegateProxy.Callback(System.Object, System.EventArgs)+0x22
10c3ec04 6613cb70 00000000 00000000 00000000 System_Web_ni!System.Web.UI.Control.OnLoad(System.EventArgs)+0x64
10c3ec04 6614e14d 00000000 00000000 00000000 System_Web_ni!System.Web.UI.Control.LoadRecursive()+0x30

再看看其他线程的:

0:015> kb
ChildEBP RetAddr  Args to Child             
0000515e 7a4b16e5 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.RegexInterpreter.Go()+0x152b
0000515e 7a4b15c3 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.RegexRunner.Scan(System.Text.RegularExpressions.Regex, System.String, Int32, Int32, Int32, Int32, Boolean)+0xa5
0000515e 7a4b14af 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.Regex.Run(Boolean, Int32, System.String, Int32, Int32, Int32)+0x103
ffffffff 7a4dc313 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.Regex.Match(System.String, Int32)+0x1f
ffffffff 7a4d1c39 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.RegexReplacement.Replace(System.Text.RegularExpressions.Regex, System.String, Int32, Int32)+0x47
05f79678 7a4d1b53 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.Regex.Replace(System.String, System.String, Int32, Int32)+0x99
05db3568 0ff965c7 00000000 00000000 00000000 System_ni!System.Text.RegularExpressions.Regex.Replace(System.String, System.String)+0x2f
05db3568 0ff96530 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Text.HtmlHelper.RemoveHtmlComments(System.String)+0x67
00000000 0ff96477 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Text.HtmlHelper.RemoveHtmlAndComments(System.String)+0x38
0140f0d8 0ff96142 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Components.AggregatedPost.set_Description(System.String)+0x2f
0140f0d8 0ff95d8e 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Data.DataHelper.LoadAggreatedPost(System.Data.IDataReader)+0x1e2
0140f140 0ff95c02 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Data.DatabaseObjectProvider.GetAggregatedPosts(Int32, Int32, Int32)+0x126
0140f188 0ff9e4eb 00000000 00000000 00000000 3rdparty_Framework!3rdparty.Framework.Data.Cacher.GetPagedAggregatedPosts(Int32, Int32, Int32, 3rdparty.Framework.CacheDuration, Boolean)+0x14a
0140f1e0 0ff9e364 00000000 00000000 00000000 3rdparty_Blog_Web!3rdparty.Blog.Web.WebForm1.BindData()+0x16b
0140f434 66f12980 00000000 00000000 00000000 3rdparty_Blog_Web!3rdparty.Blog.Web.WebForm1.Page_Load(System.Object, System.EventArgs)+0x44
0140f434 6628eff2 00000000 00000000 00000000 System_Web_RegularExpressions_ni!System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)+0x10
0140f434 6613cb24 00000000 00000000 00000000 System_Web_ni!System.Web.Util.CalliEventHandlerDelegateProxy.Callback(System.Object, System.EventArgs)+0x22
0140f434 6613cb70 00000000 00000000 00000000 System_Web_ni!System.Web.UI.Control.OnLoad(System.EventArgs)+0x64
0140f434 6614e14d 00000000 00000000 00000000 System_Web_ni!System.Web.UI.Control.LoadRecursive()+0x30
0140f434 6614d8e3 00000000 00000000 00000000 System_Web_ni!System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)+0x59d

其他两个线程和这两个类似。

偶感觉是在太幸运了,居然碰到这么一个simple的high cpu,哈哈!那看一下,为什么Regex.Replace会慢?运行一下!clrstack -a命令:

0:015> !clrstack -a
OS Thread Id: 0x898 (15)
ESP       EIP   

====================其他太长,省略……=============================

0140f034 7a4d1b53 System.Text.RegularExpressions.Regex.Replace(System.String, System.String)
    PARAMETERS:
        this = <no data>
        input = <no data>
        replacement = <no data>

0140f044 0ff965c7 Joycode.Framework.Text.HtmlHelper.RemoveHtmlComments(System.String)
    PARAMETERS:
        text = 0x05da9298
    LOCALS:
        <CLR reg> = 0x05db3568
        <CLR reg> = 0x00000000
        0x0140f044 = 0x00000000

====================其他太长,省略……=============================

哦,那个字符串的地址是0x05da9298,看看大小,是41K

0:015> !objsize 0x05da9298
sizeof(05da9298) =        41,680 (      0xa2d0) bytes (System.String)

如果do这个字符串的话,我们能看到它是一个html文本。因为runaway和.time和threadpool的数据太明显,so,断定问题就在于replace的字符串太大或者频度太高造成的。

后来经过确认,RemoveHtmlComments代码中的Regex.Replace没有用,so,直接comment掉了,问题解决。

额外看一下别的东西:

 

Address         MT     Priority         Create Time             Expires        Last Updated  Key Class Name
01b1c6cc  6639d580 Normal 03/09/2008 06:12:14 12/31/9999 23:59:59 0   0  dmachine/webroot/2/app_code System.Web.CachedPathData
01b1f470 6639d580 Normal  03/09/2008 06:12:14  12/31/9999 23:59:59  0 dmachine/webroot/2/global.asax System.Web.CachedPathData
01b3b3b0 6639d580 Normal  03/09/2008 06:12:15  12/31/9999 23:59:59 03/09/2008 07:07:35  dmachine/webroot/2/systemmessages System.Web.CachedPathData

这是aspnetcache的很少一部分的内容,值得注意的是,有很多cache的内容,过期时间是9999年。实际上,我们有很大理由可以判断,“天长日久”,w3wp会因为Out Of Memory而导致recycled问题,或者是访问量骤增并持续一段时间,会导致网站短期内崩掉。

再看一下heap:

0:015> !heapstat
Heap     Gen0         Gen1         Gen2         LOH
Heap0    7291624      255092       5071228      13381296   
Heap1    7178448      440428       4637748      14825768   
Total    14470072     695520       9708976      28207064   

Free space:                                                 Percentage
Heap0    84           12           6568         2011792     SOH:  0% LOH: 15%
Heap1    2588         12           392          7699728     SOH:  0% LOH: 51%
Total    2672         24           6960         9711520    

大对象是最大的,,几乎等于gen0+gen1+gen2的总和。我们看看整体情况先:

 0:015> .foreach(myobj {!dumpheap -short -min 85000}) {!objsize myobj}
sizeof(09c07bf0) =      689,900 (     0xa86ec) bytes (System.Byte[])
sizeof(09cb02e0) =      616,976 (     0x96a10) bytes (System.Byte[])
sizeof(09e2f2a8) =      616,976 (     0x96a10) bytes (System.Byte[])
====================其他太长,省略……=============================
sizeof(0c89cf50) =      616,976 (     0x96a10) bytes (System.Byte[])
sizeof(09d7c320) =      131,084 (     0x2000c) bytes (System.Int32[])
====================其他太长,省略……=============================
sizeof(0bec8f60) =    1,114,124 (    0x11000c) bytes (System.Int32[])
sizeof(0c092a68) =    1,114,124 (    0x11000c) bytes (System.Int32[])
sizeof(09b8cb40) =      105,512 (     0x19c28) bytes ()
sizeof(09bc6780) =      263,280 (     0x40470) bytes ()
====================其他太长,省略……=============================
sizeof(0c42f080) =    4,644,560 (    0x46ded0) bytes ()
sizeof(09b26468) =      262,164 (     0x40014) bytes (System.String)
====================其他太长,省略……=============================
sizeof(0bda0f00) =      262,164 (     0x40014) bytes (System.String)
sizeof(0bfd8f70) =      262,164 (     0x40014) bytes (System.String)

byte数组:

0:015> !gcroot 0c398670
Note: Roots found on stacks may be false positives. Run “!help gcroot” for
more info.
Scan Thread 7 OSThread c60
Scan Thread 13 OSThread aec
Scan Thread 14 OSThread c68
Scan Thread 15 OSThread 898
ESP:140f088:Root:  05da5d54(System.Data.SqlClient.SqlDataReader)->
  05da5a18(System.Data.SqlClient.SqlConnection)->
  01d3a928(System.Data.SqlClient.SqlInternalConnectionTds)->
  05b84ccc(System.Data.ProviderBase.DbConnectionPool)->
  05b84f9c(System.Collections.Generic.List`1[[System.Data.ProviderBase.DbConnectionInternal, System.Data]])->
  05b84fb4(System.Object[])->
  05e03e00(System.Data.SqlClient.SqlInternalConnectionTds)->
  05e03e9c(System.Data.SqlClient.TdsParser)->
  05e03f3c(System.Data.SqlClient.TdsParserStateObject)->
  0c398670(System.Byte[])
ESP:140f0e8:Root:  05da5d54(System.Data.SqlClient.SqlDataReader)->
  05da5a18(System.Data.SqlClient.SqlConnection)->
  01d3a928(System.Data.SqlClient.SqlInternalConnectionTds)->
  05b84ccc(System.Data.ProviderBase.DbConnectionPool)->
  05b84f9c(System.Collections.Generic.List`1[[System.Data.ProviderBase.DbConnectionInternal, System.Data]])->
  05b84fb4(System.Object[])->
  05e03e00(System.Data.SqlClient.SqlInternalConnectionTds)->
  05e03e9c(System.Data.SqlClient.TdsParser)->
  05e03f3c(System.Data.SqlClient.TdsParserStateObject)->
  0c398670(System.Byte[])

其他的bye数组类似,都是在执行SqlDataReader的操作。(为啥这样,这个callstack看不到)

看int32数组:

0:015> !gcroot 09fd4e60
Note: Roots found on stacks may be false positives. Run “!help gcroot” for
more info.
Scan Thread 7 OSThread c60
====================其他太长,省略……=============================
Scan Thread 24 OSThread c64
ecx:Root:  01e51c1c(System.Text.RegularExpressions.RegexInterpreter)->
  09fd4e60(System.Int32[])
esi:Root:  01e51c1c(System.Text.RegularExpressions.RegexInterpreter)->
  09fd4e60(System.Int32[])
ESP:114ae598:Root:  01e51c1c(System.Text.RegularExpressions.RegexInterpreter)->
  09fd4e60(System.Int32[])
ESP:114ae5ac:Root:  01e51c1c(System.Text.RegularExpressions.RegexInterpreter)->
  09fd4e60(System.Int32[])
Scan Thread 26 OSThread 9ac
Scan Thread 27 OSThread 80c

这个是上面分析的正则表达式。

字符串的信息,就是上面看到的,都是html reply。针对这几种类型,我们做一个总结(在所有大对象中)

byte数组:4M,这个是sqldatareader读出来的数据

int32数组:5.8M,这个是因为regex和下面的string导致的

string:8.5M

另外:


上述所有module都是debug模式的,这里需要我们修改一下web.config,设置debug=false以及batch compilation为true,这样也能一定程度上提高performance。

没啥结论,就这么结束了。

0:015> !finddebugmodules
Loading all modules.
Searching for modules built in debug mode…

App_GlobalResources.-mywaxaw.dll not built release
App_Code.fqhaeca6.dll not built release
3rdparty.Framework.DLL not built release
App_global.asax.0y1c1i6p.dll not built release
3rdparty.Blog.Web.DLL not built release
freetextbox.DLL not built release
3rdparty.BlogML.DLL not built release
3rdparty.Extensibility.DLL not built release
3rdparty.Installation.DLL not built release
3rdparty.Plugins.Core.DLL not built release
3rdparty.Plugins.Examples.DLL not built release
3rdparty.Scripting.DLL not built release
3rdparty.Web.Controls.DLL not built release
App_Web_lab8my1c.dll not built release
App_Theme_Home.Clever.__mjadjg.dll not built release
App_Web_xppxn64l.dll not built release
App_Web_yxlgarwj.dll not built release
App_Web_a8wjdvdv.dll not built release
App_Web_ijqvv7ky.dll not built release
App_Web_xxkqxoen.dll not built release
App_Web_b25tsu-i.dll not built release
App_Web_kcb_q9mh.dll not built release
App_Theme_theme.elitecircle.jmea1orn.dll not built release
App_Web_site.master.9fb3aa03.6svjtsyu.dll not built release
App_GlobalResources.-mywaxaw.resources.dll not built release
App_Theme_theme.default.rggbez76.dll not built release
App_Web_post.aspx.9fb3aa03.ocnogniv.dll not built release
App_Web_k_ivm-xy.dll not built release
App_Web_category.aspx.9fb3aa03.lq_3jsov.dll not built release
App_Web_r8dwq35a.dll not built release
App_Web_archivemonth.aspx.9fb3aa03.jkc9sbgz.dll not built release
App_Web_gallery.aspx.9fb3aa03.vc-q4rsf.dll not built release
App_Web_contact.aspx.9fb3aa03.ayhi_kzk.dll not built release
App_Web_vocni5-f.dll not built release
App_Web_zgfpt_vo.dll not built release

Done Seaching

发表在 未分类 | 标签为 | 4条评论

用perfmon简单分析GDI+性能和代码的一点小改进


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;
12
13                ccdc.Add(totalPaint);
14
15                PerformanceCounterCategory.Create(GDI+ MonitorSome 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(00this.Width, this.Height);
 9            g.FillRectangle(new LinearGradientBrush(r, Color.Red, Color.Blue, LinearGradientMode.BackwardDiagonal), r);
10
11            g2.DrawImage(bmp, new Point(00));
12
13            g.Dispose();
14            g = null;
15
16            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);
 9
10            paintcall.Increment();
11
12            Rectangle r = new Rectangle(00this.Width, this.Height);
13            g.FillRectangle(new LinearGradientBrush(r, Color.Red, Color.Blue, LinearGradientMode.BackwardDiagonal), r);
14
15            g2.DrawImage(bmp, new Point(00));
16
17            g.Dispose();
18            g = null;
19
20            bmp.Dispose();
21            bmp = null;
22        }

注意第三行,我们判断,如果在resize过程中,那么就直接返回。这样,虽然界面像白板一样,但是却少了很多Paint操作,从性能上会好不少。

发表在 未分类 | 标签为 | 4条评论

GDI+的leak


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:这个值每次会增加1000
3、你连续点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上查一下。

发表在 未分类 | 标签为 | 11条评论

Windbg入门及提高,我的评价(广告续)

1、这本书对于初学者没有太大用处
2、这本书对于眼中只有架构、自己不写程序的、鄙视代码的人没有用处
3、这本书对于非微软的人用处不算太大,你不知道ms内部的数据结构,你没有private symbols。
4、这本书对于微软的人用处不算太大,搞debug的就那么几号人


5、这本书对于在客户现场被骂的狗血喷头的、自己即使架了.NET IDE也不知道如何找出问题的人很有用处


 


如果你是第五种人,疯狂购买吧!

发表在 未分类 | 标签为 , | 4条评论

windbg入门及提高(广告)

此文是偶的偶像和哥们的,转过来,替他做一下宣传。偶会买10本,9本送人,有要的,现在报名。

(原来公司大量的COM+和.NET的case,都是熊老大做的)

 

Windows 高效排错
《Windows 高效排错》 可以在CSDN读书频道预览了


读书频道的排版有些问题,看起来不是很舒服。如果想看PDF的,可以在这里下载


纸板书籍估计在11月中下旬面世 

现在在China-pub, dearbook等网上书店已经有介绍,地址分别


您的看法非常重要。如果你看过这本书的PDF,了解本书的内容和潜在读者,请您在上面的链接中留下你的观点,便于其它不了解这本书的人选择。

如果您通过这本书介绍的方法解决过实际的问题,非常希望您能够分享您的经验。您可以发邮件到
如果您对书中所述内容有疑惑,也欢迎您写信来讨论。我会尽快回复。如果我们之间的讨论对其他读者有帮助,我也会放到网上,同时送书给您作为答谢。

===

上面是官腔了。我个人想说的话其实是:

1) 书前言里面说,”2007年年初我不再做技术支持。希望这本书能帮我记录下这一段美好的经历”,其中最让我难忘的就是跟xiao,elan和leo站在水房门口讨论case的日子
2) 常来我blog踩的同学们,你们要书的话把地址留给我。等我拿到了我给大家发。(估计要等一段时间。出版社就给我6本。剩下的我争取去批发)
3) 跟我一起做过case的,如果你需要的话也把地址留给我
4) 多卖一本书,我可以多拿3块钱不到。整本书的稿费还不够在上海买半个车牌,或者买个厕所。我一点也不关心字面上的销量。我之所以花那么多时间来做这个亏本生意,目的只有一个,就是希望跟喜欢调试的人分享其中的过程,让真正需要这方面文章的人有所参考。所以如果你身边有这本书的潜在读者,劳烦你推荐一下。

发表在 未分类 | 标签为 , | 26条评论

关于InvokeMember的一个郁闷的错误

背景:两个CustomControl,一个叫做MyPanel,是一个M*N的格子,一个叫做Ball,是一个球。球可以放在panel的格子中,可以鼠标进行drag & drop.


直接call很简单,没有任何问题。但是通过reflection,碰到了郁闷的事情。虽然最终搞定了,但是要看过InvokeMember的代码才能知道为什么。


代码:


        Object panel = null;
        Type tpanel = null;


        private void ReflectionForm_Load(object sender, EventArgs e)
        {
            Assembly asm = Assembly.LoadFile(@”D:testWindowsApplication5MyLibrarybinDebugmylibrary.dll”);
            tpanel = asm.GetType(“MyLibrary.MyPanel”);
            panel = tpanel.InvokeMember(“”, BindingFlags.CreateInstance, null, this, new object[] { });

            (panel as Control).Location = new Point(100, 100);
            this.Controls.Add(panel as Control);                          
        }


出错的地方:


tpanel.InvokeMember(“AddBall”, BindingFlags.Instance | BindingFlags.Public|BindingFlags.InvokeMethod,
                null, panel, new object[] { obj });


如果上面的panel是object类型,那么一切正常。如果是Control类型,即,我声明为:Control panel,然后
object obj = tpanel.InvokeMember(“”, BindingFlags.CreateInstance, null, this, new object[] { });
panel = object as Control;
panel.Location = new Point(100,100);
this.Controls.Add(panel);


那么上面的InvokeMember就会提示我:Method not found!如果我修改BindingFlags,去掉Method,那么没有MethodNotFound的exception,但是会有“请使用InvokeMethod,Set/Get Field之类的提示”。如果加上,就是MethodNotFound,真是faint!


google了一些,貌似有些人碰到过,一个post上面说,arguments的类型必须要和method的类型完全一致才可以。我想,如果Panel当作control来用,那么control -> Ball类型会无法转换的,而object -> Ball是允许的。


哪位高手帮忙解释一下?谢谢!

发表在 未分类 | 标签为 | 9条评论

微软招聘SQL高手

(不好意思,置顶几天)


微软招聘SQL专家,如果您认为在下述方面有专长,请积极报名:  


1、工作地点:上海;
2、很强的微软技术背景和产品熟悉度;
3、很强的客户沟通能力;
4、熟悉 SQLServer,熟悉Reporting Service开发、维护
5、熟悉 BI理念、产品,如果有 SharePoint Portal Server 2003 或 Office SharePoint Server 2007 实战经验将优先考虑;

请把个人简历发给我:

发表在 未分类 | 标签为 , | 8条评论