https://www.cnblogs.com/junchu25/archive/2012/08/10/2631422.html

上周四产品上线一切运行正常,做了一点小改动后周四晚上发布,周五大量用户反馈在访问页面时出现长时间等待响应。将4台Web前端的服务器重启,缓存服务器2台重启,问题依旧。由于是生产环境,只能上去查看IIS、Windows日志,没有记录任何异常。于是将版本更新回周四凌晨发布版本,运行正常。据版本跟踪只修改过一个特定模块,但是这个模块不会影响页面的正常访问。于是猜测可能是短时间的网络问题,IIS在这个时间段没有请求访问的日志,不会是程序的性能问题。于是晚上在集成环境恢复周四晚上发布的版本,问题重现,但是周四早上的版本没有问题。

任务管理器显示w3p进程的内存、cpu使用率、线程数都没有变化,应该是一个deadlock。但是周四晚上发布的版本并没有添加或修改过线程同步的代码。对发布的dll进行跟踪排查,发现Memcached.ClientLibrary.dll的大小和早上发布的版本不同。晚上发布的是Debug版本,将Memcached.ClientLibrary.dll更新为Release版本问题解决。于是问题就产生了,为什么Debug和Release版本在同一个环境下,Debug版本会造成deadlock。

用WinDbg附加到w3p进程,进行压力测试,30 – 50用户下出现deadlock。用!runaway查看线程使用cpu时间,大量线程使用cpu时间为0。用~* kb查看所有call stack,大量线程都在等待ntdll!NtWaitForMultipleObjects。用!cs查看临界区,内容太多。修改HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\CriticalSection-Timeout的值为60,便于WinDbg获得更多的超时信息。加载sos.dll,!threads查看所有托管线程,发现一个线程抛出了System.Threading.SynchronizationLockException。用!SyncBlk查看托管代码的lock情况,发现存在两个实例lock和一个RuntimeType lock。继续用~* e !clrstack查看所有的托管线程callstack,发现大部分代码停留在SockIOPool的CheckIn函数,小部分停留在SockIOPool的GetInstance函数和RemoveSocketFromPool函数。根据!SyncBlk显示的三个线程ID,用~线程ID s命令切换到对应线程查看callstack。使用实例lock的线程停留在RemoveSocketFromPool函数,使用RuntimeType的线程停留在CheckIn函数,抛出SynchronizationLockException的线程则停留在GetInstance函数。

于是分析Memcached.ClientLibrary.dll的源代码,发现它在几个函数上使用了MethodImplAttribute,并将函数的属性标示为Synchronized。对于MethodImplOptions.Synchronized,如果函数为static那么它使用RuntimeTypeHandle作为lock,实例函数则使用this指针作为lock。CheckIn为实例函数,而GetInstance、RemoveSocketFromPool为static函数。由于内部代码创建了两个SockIOPool实例,所以在callstack中可以看到它们分别停留在RemoveSocketFromPool函数(CheckIn函数内部会调用RemoveSocketFromPool函数),证明有两个线程独占了this指针。而另一个线程停留在了CheckIn函数,在CheckIn函数前会先调用GetInstance函数,所以它lock了RuntimeType,但正常情况下在调用完GetInstance函数后不应再独占资源。这就是一个典型的deadlock,有两个线程锁住了this指针同时在等待获取RuntimeTypeHandle,而一个线程锁住了RuntimeTypeHandle,同时在等待获取this指针。

知道了现象后情况就很容易模拟,编写一个Console程序部署到x64的机器上运行,问题重现。但是这个问题在x86的机器上没有问题、在x64机器上以x86的兼容性运行也不会存在deadlock。于是大致情况可以确定在x86的机器上编译的Debug版本在x64机器上运行会出现deadlock。通过ildasm查看Debug和Release在IL层级的区别,发现MethodImplOptions.Synchronized会被修饰为cil sync managed,看来这层操作只有在JIT后才会看出代码的区别。

这个时候在Microsoft的Connect上发现有人曾经在09年4月份反馈这么一个现象,当时给出的workround就是使用显示的lock代替MethodImplOptions.Synchronized,同时会在下一个版本的CLR修复。为了验证是否在下一个版本的CLR修复,将同样的测试代码运行在.NET Framework 4.0下没有问题。

警惕32位程序在MethodImplOptions.Synchronized在x64机器上的同步缺陷[z]的更多相关文章

  1. 64位系统上运行32位程序能否申请到8G内存?

    申请不到,因为64为系统在运行32位程序的时候只是为了向下兼容而已,对于32位程序来讲,申请8G的存储空间没有任何意义,因为32位的程序最大寻址空间只有4G,32位程序在编译之后的机器代码也只有32位 ...

  2. 转:如何在32位程序中突破地址空间4G的限制

    //如何在32位程序中突破地址空间4G的限制 //首先要获得内存中锁定页的权限 #define _WIN32_WINNT 0x0501 //xp系统 #include <windows.h> ...

  3. 64位Ubuntu运行32位程序时报文件不存在(No such file or Directory)的一种解决办法

    尝试在64位Ubuntu下面运行32位程序时, 一直说 文件不存在(No such file or directory), 我只想说++. 你tm说个文件格式不正确不就好了? 非得说个文件不存在! 真 ...

  4. 记32位程序(使用3gb用户虚拟内存)使用D3DX9导致的一个崩溃的问题

    为了增加32位程序的用户虚拟内存的使用量,我们使用了/LARGEADDRESSAWARE编译选项来使32位程序可能使用到3gb的内存,能否使用到3gb内存也跟平台.系统和设置有关系,现摘抄部分作为参考 ...

  5. Ubuntu14.04 64位运行32位程序

    最近公司新增的机器安装Ubuntu14.04 64bit导致之前在32bit下编译的Qt工具软件无法运行. 于是google的了一下找到一些解决办法,但不能保证全部32bit的Qt程序都能正常,测试了 ...

  6. Linux 64位编译\链接32位程序

    测试机器:Ubuntu14.04 64位 gcc编译32位程序,添加参数-m32: $ gcc -c -fno-builtin -m32 TinyHelloWorld.c ld链接32位代码,添加参数 ...

  7. "用wow64exts调试64位任务管理器抓取的32位程序的dump"

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:"用wow64exts调试64位任务管理器抓取的32位程序的dump".

  8. 关于32位程序在Win7&64位系统中连接Microsoft Excel数据源的问题

    最近在新公司电脑上跑以前的selenium测试框架的时候,抛出了如下的错误 出现的是ODBC Driver问题:[Microsoft][ODBC Driver Manager] Data source ...

  9. 32位程序调用Oracle11gR2数据库libclntsh.so失败

    [问题描述]32位程序调用Oracle11gR2数据库的libclntsh.so库时会返回失败. [问题原因]32位程序只能调用32位的Oracle客户端实例包,而R2数据库默认安装完毕后是没有lib ...

随机推荐

  1. ef6.0+mysql配合使用的问题

    折腾了很久由于所用到的各种库版本问题:后来终于组合成了一个可用的:记录下各种库的版本 ef6.0 mysql5.5 mysql-connector-net-6.9.12.msi mysql-for-v ...

  2. 手写实现ArrayList & LinkedList

    微信公众号:程序媛的非程序人生 关注可了解更多资讯.问题或建议,请公众号留言; 1.手写实现ArrayList     2.手写实现 LinkedList       3.LinkedList的数据结 ...

  3. 接口测试 - ti

    脚本 主程序 #!/bin/bash . /etc/ti/ti.conf . /etc/ti/ti.fun #-basic.json | curl -H "Content-Type:appl ...

  4. javascript条件语句

    //条件语句 if (false) { console.log("is true") } else { console.log("is false") } // ...

  5. FPGA——入手(零)

    前几天正点原子团队退出了FPGA开发板,我就买了一套.我想的是,多学一点东西,即使到最后积累下的东西少,但是面是广的,以后可以更好的选择一种深入.就入手了新起点FPGA开发板,新起点算是开拓者的阉割版 ...

  6. Tinker热修复

    集成buggly热修复的时候报错 Error:A problem occurred configuring project ‘:app’. Failed to notify project evalu ...

  7. 网上Java总结

    1.         编译 javac 文件名.java 运行 java 文件名 2.         Public class 和 class 声明类的区别:public 声明的文件名必须和其类名一 ...

  8. spring AOP capbilities and goal

    Spring AOP 是用纯JAVA 实现的. 不需借助JAVA代码在编译处理阶段来实现. Spring 是在运行期实现的.AOP的实现可以在编译,加载,运行三个阶段来实现:Spring AOP 也不 ...

  9. Form-encoded method must contain at least one @Field.

    https://blog.csdn.net/liunian823/article/details/80290855 记得之前遇到过这个问题,并且记录笔记了,这次再翻笔记,却没有找到...搜索 了下. ...

  10. file常用功能

    构造方法 File(String pathname):将指定的路径名转换成一个File对象 File f = new File("D:\\a\\b.txt"); File(Stri ...