百度空间 | 百度首页 
 
查看文章
 
《Windows用户态程序高效排错》1
2009-03-10 09:03
作者BLOG http://eparg.spaces.msn.com/blog

C# struct实例 在stack 还是 heap上? --!address
《Windows用户态程序高效排错》 中涉及到的链接

"重现" Time Travel Debugging 代号iDNA

http://blogs.msdn.com/cse/attachment/1077668.ashx


reflector and profiler

CLR Profiler for the .NET Framework 2.0


http://www.microsoft.com/downloads/details.aspx?FamilyID=a362781c-3870-43be-8926-862b40aa0cd0&DisplayLang=en
No Code Can Hide from the Profiling API in the .NET Framework 2.0


http://msdn.microsoft.com/msdnmag/issues/05/01/CLRProfiler/default.aspx
CLR Debugging vs. CLR Profiling

http://blogs.msdn.com/jmstall/archive/2004/10/22/246151.aspx

关于:平衡、取舍、双赢


RFC 1925 (RFC1925)


http://www.faqs.org/rfcs/rfc1925.html

http://blog.csdn.net/eparg/archive/2007/09/19/1791954.aspx


ShellExecute 内部调用栈 ,需要维护自已的消息循环,通过DDE打开目标文件,其内部是依靠windowsMessage来完成进程间通信。


1.2.5结论


1。线索

2。对比


DWORD 文件长度 4G

0Xcdcdcdcd访问未初始化的内存。
1.3.3
JavaScript维护事务逻辑 ,WEB开发大忌,
重定向最好用HTTP 302(如Response.redirect)。

第二章
汇编、异常、内存、同步和调试器--重要知识点和神兵利器
exception、内存布局、heap stack CRT handle Criticalsection/thread context/windbg/dump/ live debug/Dr Watson
两本书:
Programming Application for Microsoft Windows ,Jeffrey Richter ,MS press 1999
Debugging Applications for Windows ,John Robbins,MS Press,2003
windbg:
DebugInfo:
http://www.debuginfo.com/

Windows Debuggers:
http://www.codeproject.com/debug/windbg_part1.asp

PDB包含类型及符号所处二进制地址。

!address检查对应内存页的属性,由操作系统维护,需要加载系统模块PDB。
x 显示符号的二进制地址(如函数),显示局部变量。
vertarget显示当前进程的大致信息。
!peb显示 process Environment Block
lmvm 检查模块加载信息
.reload /!sym加载符号文件。
lmf 列出当前进程中加载的所有模块。
r显示和修改寄存器上的值
d 显示内存地址
e 修改内存地址上的值。
dd (DWORD),db(byte),du(Unicode),dc(char)
s 搜索内存
!runaway 检查线程的CPU消耗
~切换线程
~0s
~*k显示所有线程的call stack
u 反汇编
uf 直接反汇编整个函数
x查找符号的二进制地址
x ntdll!*
dds 对应二进制地址 的符号
找到虚函数表中的具体的函数地址。
先x XXX!XXX::'vftable' 再dds结果地址。
.frame栈中切换以便检查局部变量
dt 格式化显示资料
-b -i 显示内部类 和数组
dt 0x00XXXX MyCls,将任意地址按指定类型来格式化显示。
2.1.6 live debug
wt : watch and trace data ,可以跟踪一个函数的所有执行过程,并给出统计信息。
观察函数执行过程和分支。或者评估性能。
断点 和条件断点
bp +地址和函数名
ba 设定访问断点,在某地址被读写时断。
Exception断点,sx小结
sxe av Access Violation 停
sxn ld DLL LOAD仅输出
sxd eh C++ Exception 不处理
poi(取地址上的值)类C中&.
0n十进制前缀
ba w4 excep!i " j (true?false ) '.echo skip; g ' ;' .echo stop!' "

Step out 的实现
定义是:target executes until the current function is complete." 可以简单地在当有函数的返回地址上设定断点。返回地址保存在函数入口时EBP+4上。如直接设,
1。无法区分递归,及其他线程对该地址的调用。
2。第一次触发后不会自动清除端点,可能会多次触发。
bp /1 /c @$csp @$ra;g
/1使断点在触发后自动消除;避免问题2
/c @$csp 参数通过指定 callstack 最小深度避免问题1 @$ra 直接表示当前函数的返回地址。
远程调试
.server 本地创建TCP端口或通过named pipe,双方均可输入命令。执行结果都显示。只是简单的通过重定向方便远程检查,而实际调试工作都发生在目标机器上。
另一种更强大方法,是使用DbgSrv,是一个调试服务器,让必要的调试动作发生在目标机器上,而调试功能,如加载PDB符号,发生在调试员机器上。在其出现前,调试系统核心服务如,lsass.exe,要同时结合用户态和内核态调试。过程大为简化。
Debugging LSASS ... oh what fun, it is to ride..
http://blogs.msdn.com/spatdsg/archive/2005/12/27/507265.aspx
2.1.9 Dump文件
进程的内存镜像,保存程序的执行状态。
.dump /ma c:\a.dmp

2.1.10 CDB\ NTSD
NTSD位于 system32目录下,不需要特别安装。
三者命令完全一样。
都使用同样的调试引擎dbgeng.dll
Symbols and Crash Dumps
http://msdn.microsoft.com/msdnmag/issues/02/06/Bugslayer/
由于CDB和NTSD 采用命令行标准输入输出,通过重定向控制这两个工具。
典型用例,可以把用户态的调试重定向到kernel Debugger.仅需要一个Debugger Session就可同时控制核心态和用户态的调试例程。见帮助中CDB 小结。
2.1.11
Debug Tutorial Part 4: Writing WINDBG Extensions
http://www.codeproject.com/debug/cdbntsd4.asp

2.3异常 和通知
异常的类型是通过异常代码标识。
弄清异常发生的时间、地点、导致异常的指令和异常结果,对排错至关重要。
1st chance ,2nd chance 是针对调试器而言的。
异常发生后,操作系统在调用用户态异常处理函数前,会检查是否有调试器加载,首先把异常信息发给,使其有第一次观察机会 。调试器处理完后,才让用户态程序处理。
如果用户态程序处理此异常,无关调试器,否则在unhandled excption崩溃前,操作系统给调试器第二次观察异常的机会。
操作系统提供的异常处理功能叫 Structrued Exception Handle (SEH),C++和其他高级语言的异常处理机制都是建立在SEH上的。如果要直接使用SEH,可在C/C++中使用__try __except关键字。
A Crash Course on the Depths of Win32™ Structured Exception Handling
http://www.microsoft.com/msj/0197/Exception/Exception.aspx
RaiseException
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/raiseexception.asp
C++像C#一样打印函数调用栈
SEH,DEP, Compiler,FS:[0], LOAD_CONFIG and PE format
http://eparg.spaces.msn.com/blog/cns!59BFC22C0E7E1A76!712.entryhttp://blog.csdn.net/eparg/archive/2007/09/19/1791966.aspx
2.3.2 Adplus ,抓取dump的方便工具
dump记录进程某一时间的具体信息,保存时机非常重要。
程序崩溃,应该选在引发崩溃的指令执行时,也就是1st chance,时获取,能够看到问题的直接原因。
VBS脚本
windows api MiniDumpWriteDump API ,可供生成MINI DUMP。

Description of the Dr. Watson for Windows (Drwtsn32.exe) Tool
http://support.microsoft.com/?id=308538
Specifying the Debugger for Unhandled User Mode Exceptions
http://support.microsoft.com/?id=121434
INFO: Choosing the Debugger That the System Will Spawn
http://support.microsoft.com/?id=103861
IFEO劫持(Image File Execution Option Hacking)
WINDOWS提供让指定进程随调试器启动的选项
1。IFEO下创建程序同名键
2。此键下创建Debugger字符串类型子键
3。设定Debugger=autodump.bat
4bat 内容: cscript.exe adplus.vbs -crash -o *.dmp -quiet -sc %1
Dr. Watson
1。运行drwtsn32.exe -i 注册 。
2。打开AeDebug 注册表,找到Debugger 项,值应为:
drwtsn32 -p %ld -e %ld -g.
3修改为:
windbg.exe -p %ld -e %ld -c ".dump /mfh *.dmp;q"

2.3.3 Debug Event
操作系统跟调试器交流的方法。
Controlling Exceptions and Events主题,有所有代号。
DLL 加卸载,线程创建和退出。
sxe ld:*.dll截获Moudle Load通知。

2.3.4
崩溃(二次机会时)
手动恢复exception context.
异常对话框是通过UnhandleExceptionFilter函数显示。其参数包含异常信息。此时,通过.exr .cxr可恢复。
kb
UnhandleExceptionFilter首参数保存异常信息和异常上下文的地址。
dd 0x首参数
异常信息 异常上下文的地址
.exr 异常信息 地址
.cxr 异常上下文的地址, 切换上下文后,
kb 看到异常发生时候的状态。

未处理异常发生后主动退出
使用此项技术的有COM+ ASP.NET 旺旺客户端
好处:
1。自定义接口。
2。异常详细信息保存以便分析。
3。挽救工作,重启。
实现:
1。使用SEH __try __except
2。使用UnhandleExceptionFilter,崩溃后发送异常。
缺点:
调试器无法接收2cn chance exception,如com+ crash。

避免调试器追踪
把代码放至SetUnhandleExceptionFilter设定的函数里。人为触发 Unhandle Exception。因为UnhandleExceptionFilter仅在调试器不加载时才会被调用。
2.4 Heap and Stack

WIN API中有两类: 内存分配函数 VirtualAlloc和HeapAlloc。前一种向操作系统申请4K为边界的整块内存.后者是分配任意大小的内存块。区别在于后者的实现依赖于前者。

操作系统管理内存的最小单位是4K。用户态需要在此基础上,提供以字节为粒度的内存分配,释放功能,并平衡好时间利用率和空间利用率。
WINDOWS提供Heap Manager完成上述功能,其工作方式:首先分配足够大的4KB倍数的连续内存空间;在这块内存上开辟一小块区域来做薄计;将其分割成尺寸不等的小块,同时小块信息记表到薄计里,记录每一小块的起始地址和长度,以及是否已分配。
内存请求发生时,根据请求长度,在薄记里找大小最合适的空间,将其标为已分配后,返回其地址,完成一次内存分配。如找不到足够大的空闲小块,以4KB为粒度向系统申请更多内存。

1。分配的内存最好是在4字节边界上,提高内存访问效率。
2。做好线程同步,保证多线程同时分配内存时不出错。
3。灵活合并拆分块。

HeapFree释放的空间,继续操作不会访问违例,除非那块地址VirtualFree返还给操作系统了。
2。越界的写操作,可能破坏薄计。导致错。
3。同一地址多HeapFree。致错。
错误之所以没有在第一时间暴露:
1。Heap每块内存的界限是由Heap Manager定义的。而内存访问无效的界限,是操作系统定义的,哪怕访问越界,如果越界的地方已有映像上来的4KB内存页,程序不会崩溃。
2。为效率,Heap Manager不会主动检查自身数据结构是否破坏。
为方便检查,让现象尽早表现,Heap Manager应该这样管理内存:
1。把所有的Heap内存都分配到4KB页结尾。并把下一个4KB页面标记为不可访问。当越界时,访问无效,立崩溃。
2。主动检查数据结构是否被破坏。
使用 Pageheap。
Debug Tutorial Part 3: The Heap
http://www.codeproject.com/debug/cdbntsd3.asp
2.4.2 Pageheap
Heap Manager的确提供主动检查错误的功能。只需在注册表里修改,操作系统会根据设置改变其行为。
Pageheap是用来配置该注册表的工具。
How to use Pageheap.exe in Windows XP and Windows 2000

http://support.microsoft.com/kb/286470/en-us
Pageheap Gflag Application Verifier都是修改注册表的工具。
内存碎片Fragmentation
内存被分割成很多小块,难找到连续的内存满足大块的内存申请。导致原因:加载过多的DLL,小块HEAP的频繁使用。
DLL分割常见情况:ASP。NET中的batch compilation没有打开,导致每个页面都会被编译成一个单独的DLL文件。
Stack overrun/corruption
Stack overrun,一般是递归函数缺少结束条件导致,函数调用过深从而把stack地址用光。
Stack corruption 往往是stack buffer overflow导致,此BUG不单程序崩溃,还会严重威胁系统安全性。
当前计算机架构上,stack是保存运行信息的地方,损坏后,所有信息丢失,调试器无用武之地。
windbg 中EIP EBP指向非法地址,说明callstack信息已被冲毁。找不到任何线索调试。有效方法 ,可疑函数中写LOG。
Pageheap 别外一功能trace,作用记录Heap历史操作。激活此功能后,Heap Manager会的内存中开辟一块专门的空间记录每次Heap的操作。

激活Pageheap 后,Heap Manager检测到错误,会激发一个break point exception,debugger停下来。

!heap -p -a free函数参数

打印出Heap Manager保存的HEAP地址的历史操作,
“dcda"-- heap内存标志位

利用这个特点来解决memory leak和fragmentation。
s -w 0 L?60030000 0xdcba
dds poi( 上面结果地址 - 6) 找到指针分别对应的callstack.
如果是固定的callstack分配,很有可能是泄漏根源。
2.5排查问题的对应层次
几千行C程序少量内存泄漏。
简单高效的办法采用CRT 的debug heap。
Heap API本身没有提供检查内存泄露的功能。
CRT的实现中,包装Heap API完成这样的功能。
恰当的层次设计,能给排错带来灵活性。
2.5.3 BSTR Cache ,建立在Heap之上的COM字符串内存管理。
OLE的内存管理跟操作系统的HEAP是在两个层次上的。
pageheap保证操作系统上的heap问题及时暴露,但ole内存管理器包装了一次,OLE缓存了操作系统分配出来的heap,进行第二次分配和管理。
如果错误的第一现场是OLE内存管理,后导HEAP,就无法用pageheap。

在OLE层禁止CACHE,直接对应HEAP分配。

CRT也是二次包装。

2.6多线程对资源的竞争:同步和锁
2.6.1句柄泄露、死锁、线程争用。
同步是为了多线程下避免资源竞争。提高系统的整体性能和可伸缩性。
通过Kernel Object、Critical Section和windows Message来实现。
句柄泄露 Handle leak
handle数量越来越多,没及时调用CloseHandle API,
如CreatThread后,未调用CloseHandle 。
解决思路:观察handle增长。找到增长的handle是何类型,如何创建出来的。
死锁(Deadlock)
在WaitForSingleObject或者是EnterCriticalSection上不返回。用户态多个线程互相依赖,互相等待。
hang归为死锁.
案例:
MFC异步网络操作,使用CAsyncSocket类。对特定请求无返回,UI僵死。
CAsyncSocket的实现是依靠windows Message派发消息、调用客户自定义的网络处理函数。
在处理请求时,客户主线程会等级一个EVNET,这个EVENT会在客户的网络处理函数中激活。
问题在于客户调用WaitForSingleObject在主线程等待时,主线程的Windows Message队列就停止派发了,这样
CAsyncSocket接受到网络包但没办法通过Windows Message通知自定义处理函数执行。自定义处理函数不执行,Event就不触发。主线程就不会结束等待。
解决方法:等待Event时,用MsgWaitForSingleObject,保持消息队列。
死锁的共同点就是应该运行的线程,在等待某种资源,这个资源可能跟 CriticalSection相关,也可能跟Windows Message相关,可能跟网络等相关。CUP=0%
对死锁排错,
1。在等什么?
2。等待的时机是否恰当?
3。等待的东西什么时候释放?
线程争用
现象
1。启用太多工作线程
2。等待同一对象。
后果性能下降。CPU,切换线程上下文开销大,
线程争用常是设计上的缺陷导致。
System\context Switches/sec
2.6.2 windbg对应排错
!handle可获取整个进程或某个。
!htrace 中获一个handle创建时的call stack,功能类似pageheap,需修改注册表来激活。仅能用于live debug,dump不存。
!cs 获取一个 CriticalSection。
Application Veifier让操作系统主动提供关于handle,CriticalSection,heap等系统层面的调试信息。
~~[]可把ID转换成线程号。
能够让WINDBG LIVE DEBUG时,通过break point机制找到误用的地方。尽早暴露问题。
!sos.savemodule保存模块。
2.7调试和设计
排错其实是抓取信息,分析信息的过程。入手的关键在于抓取恰当的信息。利用系统提供的功能来获取恰当的信息。
留下排错的方式,LOG或DEBUG版本。
为方便调试,开发应:
1。多使用assert,trace;
2。合理添加LOG;
3。每次编译后,把PDB分不同的版本,保存在安全的地方。
设计多线程环境下的安全而高性能的LOG操作不太现实。既要安全,又要高效,还要支持多线程,直接使用数据库来记录LOG。
第3章 .NET Framework的原理和SOS调试
CLR运行的关键,调试技巧。
3.1MetaData JIT GC Exception
最好的参考书:Essential.NET Volume 1 - The Common Language Runtime.
托管Assembly本身仅包含CLR可识别的元资料,不包含机器指令。
使用depends工具可观察到所有托管Assembly都跟随mscoree.dll绑定,功能是选择合适的CLR Excution Engine加载。还负责判断应该使用何种GC Flavor.
多版本CLR可共存。CLR的目录在C:\WINDOWS\Microsoft.NET\Framework.
当前系统中最新版本的CLR对应的mscoree.dll文件被拷贝到SYSTEM32目录下。
引擎加载后,首先初始化引擎需要的各种功能,要必要的全局变量,引擎需要的模块(ClassLoader\Assembly Loader\JitEngine\Context等)启动 Finalizer 和GC线程。创建System AppDomain &Shared AppDomain,创建RCDebuger,线程,加载CLR基础类(mscorlib.dll&system.dll)。
当CLR引擎初始化后,CLR会找当前EXE的元数据,找MIAN函数,编译MIAN 函数,执行。
JIT动态编译
无论函数是否已经编译成机器代码,call指令都是把执行指向跟函数相关的一个内存地址(stub),如果未被编译,stub 中的代码执行定向到CLR JitEngine,编译。

每种方法,只需要编译一次,编译完成后,CLR把编译好的机器代码拷贝到进程中的由CLR管理的某一块内存,然后,JitEngine把编译好的函数入口地址回填到stub中。第二次调存时,已指向已编译好地址。CLR支持NGen,库函数默认已NGen。

调用非托管代码两种情况,
一。系统API和DllImport,COM interop。
二。调存CLR 的功能,比如内存分配、异常派发。
也都使存stub技术。
第一种,不管是PInvoke还是COM Interop发生时,托管代码调用的都是CLR创建的stub。stub把控制权交给对应的非托管代码。把必要的函数参数拷贝到非托管的内存上,marshal必要的类型,锁住交互的托管内存区域,防止GC移动。如是COM,还要QueryInterface。当非托管调用完成后,执行权返回到stub,再回托管。

第二种情况,对CLR功能的调用往往是隐式发生的。

一类是编译器直接生成CLR stub的调用。比如 new /throw关键词。动态编译引擎对这些关键词的处理是生成函数调用到特殊的stub,它再把执行定位到CLR引擎中的关键函数。
另一类是通过把托管代码标示为internal call来编译。表示认托管函数其实是某unmanaged函数的映像,编译引擎在编译时,会直接跟unmanaged函数实现对应起来。该对应关系是在CLR的实现中通过C++的一张静态表定义的。
3.1.3 GC内存管理
CLR引擎初始化的时候会操作系统申请连续内存作为managed heap.
1.大多数情况下比非托管代码内存分配速度快。
2。GC发生时CLR可以对托管OBJECT进行随意移动,再修正保存object 的stub信息。保证托码不受影响。防止内存碎片。
3。当线程的执行状态不会受到GC影响的时候,该线程的PreEmptive GC属性是1,否则是0。此开关受CLR的控制,很多stub中的代码会操作这个开关。当线程idle的时候,PreEmptive 为1。当GC触发时,GC必须等到所有的线程都进入PreEmptive后,才可以发生。
3.1.4Exception Handing异常处理
CLR可以通过元数据采集所有的类型,在thread中通过多种机制记录运行状态。
经典!关于CLR类型加载,动态编译,异常处理等细节,及CLR内部类型(MethodTable,MethodDesc,EEClass,SharedDomain等),
Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects
http://msdn.microsoft.com/msdnmag/issues/05/05/JITCompiler/default.aspx
CLR牛人的blog
http://blogs.msdn.com/cbrumme/
3.2 用Windbg探索CLR的实现
3.2.1 开源CLR实现,Rotor
MS提供,稍简化的.net framework的实现。包括全部核心功能,和简化掉的外围功能。80%以上的源码跟发布版一致。可在WIN平台和FREEBSD平台编译。
Shared Source Common Language Infrastructure 2.0 Release
http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE17-3121B4F51D4D&displaylang=en
3.2.2CLR重要部分包括:
1.AppDomain的创建和跨AppDomain的方法调用。
2.Reflection方法的实现。
3.Security的实现。
4.Assembly的寻找和加载。
5.ThreadPool的实现和管理。
6.托管object的类型信息和如何绑定到object上。
7.CLR中lock的实现。
8.异常的派发和处理。
9.stackwalk和frame的作用。
10.PInvoke和COM Interop。
Server Flavor 的GC Thread,Finalizer thread和DebuggerRCThread.
Special threads in CLR
http://blogs.msdn.com/yunjin/archive/2005/07/05/435726.aspx
Things to ignore when debugging an ASP.NET hang
http://blogs.msdn.com/tess/archive/2005/12/20/505862.aspx

阅读源代码窍门:

1。重要的文件比较长。

2。善于在调试器中设断点进行分析,查看函数间调用顺序。

3。编译rotor,使用来运行托管代码,进行源代码调试。

4。打开LOG。

5。加入自己的代码测试。

3.3通过SOS调试托管程序

3.3.1CLR让托管程序的调试简化。

CLR程序包含的元数据,包含描述性的类型信息,所以CLR程序可以获取类型信息、函数名,支持reflection.

AppDomain是CLR的执行和访问边界,比process节省系统资源。

CLR可以做到,而C++不行:

1。反编译获得源码。

2。发生异常后CLR可打印callstack.

3.托管程序的symbol不再重要。

4。获取一个class的详细定义和所有方法。

5。列出内存中所有的CLR object.

6.对于任何CLR object,可以找到该object的类型信息。

CLR调试分种方式,一种VS IDE借助DebugRCThread通信,设定断点,查看程序变量。缺陷是无法利用CLR内存管理的优势来获取内存方面的信息。

3.3.2SOS的命令介绍

在CLR的安装目录下,可找到SOS.dll,随之发布的windbg debugger extension.SOS的调试命令通过读取目标进程中的CLR内部数据结构进行调试和分析。

!dumpstack -ee列出当前stack中所有托管方法地址,也能看到callstack.

!dumpstackobjects.列出当前线程的stack 中保存的所有的托管object,包括地址和类型。

!dumpobj(!do)

指定object的详细信息。

!dumpmt跟函数地址作参数,打印函数表信息,加上-md参数可以打印出函数表中所有函数信息。

!eeheap列出managed heap的统计信息。

!dumpheap打印出managed heap中所有对象的址址,重点检查:

1内存是否被某一种类型的object 占用。

2。某一种类型的object数量是不是特别多。

3。不同类型的object 是否有倍数关系。说明类型间有引用关系。

!gcroot

对于任何一托管对象,是否能够被GC收集,取决于其是否有root.可以搜指定object的root。将整个引用链打印。

一个object 要成为root,可能如下:

1.class的static成员。CLR会把其保存在一个object Array中,用pinned handle保存。

2.维护程序运行的必要成员,比如AppDomain本身,这类object会被CLR直接用strong handle保护起来。

3.stack上的成员。说明该object还在被某一个thread使用。

4.被托管代码直接pin起来的object.

!objsize该对象所引用的下级对象总大小。

!SyncBlk打印lock,对于RWLock或信号量等其他lock无效。

!soe在指定类型的CLR异常发生时中断下来执行自定义命令。

!dumplog StressLog,

!savemodule把dump中指定的module,无论是托管的还是非托管的保存到本地文件。抓到dump,相当于抓到源码。

3.4SOS演示。

.load SOS.dll

.cordll版本匹配信息。

CLR优秀的平台,通过关键的命令就可以找到关键的数据结构,定位引发问题的Object.

!ip2md映射内存地址到托管函数名。

CLR debugging案例:

If broken it is,fix it you should

http://blogs.msdn.com/tess/

3.5.1释放COM对象的两难

ReleaseCOMObject API 减少COM对象的引用计数。

如不及是调用,释放滞留到GC发生的时候。而GC发生无固定规律。COM无法释放,Finalizer thread会堵塞,导致内存持续增长。

如果使用多个COM,且之间有依赖关系,需按特定顺序释放的话,Finalizer thread无法保证正确的顺序,可能死锁。

开发人员不一定能确定正确调用ReleaseCOMObject 的时机,例如:传递COM 对象给另一个线程,同时继续使用,哪一个线程负责调用结束引用。

问题的关键在于CLR管理内存的机制。CLR通过检查对象是否还有root来判断可否释放,而COM通过引用计数来管理。

COM Interop Wrapper Object还在托管代码和非托管代码间传递多次,更复杂!

3.5.2PInvoke应该PIN住内存防止崩溃

CLR传递一块托管BUFFER到非托管API,异步填充。用C# fixed 或MC++ __pin关键字pin住这buffer对象。防止被GC移动导致访问无效或crash。 这类问题难寻根源,要有一个重现环境,配合LOG调试。

3.5.3pin住内存会碎片

在CLR上,不建议大量使用MC++开发的module,原因是存在不可避免的死锁可能。

在C#定义类型很麻烦,用MC++自动生成类型的简单方法:

用MC++直接写调用WIN API,使用windows.h中定义好的结构。编译后反编译成C#,可获取类型定义的代码。

增加对CLR的理解,5个著名的CLR Blog

http://blogs.msdn.com/cbrumme

http://www.cnblogs.com/flier

http://blogs.msdn.com/maoni

http://blogs.msdn.com/tess

http://blogs.msdn.com/yunjin




类别:读书笔记 | 添加到搜藏 | 浏览() | 评论 (0)
 
最近读者:
 
网友评论:
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
验证码: 请点击后输入四位验证码,字母不区分大小写
      

     

©2009 Baidu