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. 傻瓜学编程之block_3

    block 捕获自动变量的瞬间值: 注释在代码中:请参考傻瓜学编程之block_3 import "People.h" @interface People() { int your ...

  2. 通过setup.py安装项目dependencies

    一.使用方法 安装命令   $ pip install  -e  <option> setup.py 二.具体介绍 pip intall -e 举例一个setup.py $ pip int ...

  3. Linux常用命令 笔记

     Linux常用命令  笔记 一.文件处理命令 1. ls命令:显示目录文件                          -a 显示所有文件,包括隐藏文件.(all)               ...

  4. MyBatis中使用#和$书写占位符有什么区别?

    #将传入的数据都当成一个字符串,会对传入的数据自动加上引号:$将传入的数据直接显示生成在SQL中.注意:使用$占位符可能会导致SQL注射攻击,能用#的地方就不要使用$,写order by子句的时候应该 ...

  5. java类的生命周期

    https://www.cnblogs.com/aspirant/p/7200523.html 验证的内容:文件格式,字节码,符号引用,元数据 准备:给静态变量分配内存设置初始值(0) 初始化才是真正 ...

  6. Python笔记:编码问题

    1. python2的编码: python2中使用的是ASCII码,所以不支持中文,如果要在python2中写入中文编码,需要在文件头编写: #-*- encoding:utf-8 -*- 2. 不同 ...

  7. 【HDFS API编程】从本地拷贝文件,从本地拷贝大文件,拷贝HDFS文件到本地

    接着之前继续API操作的学习 CopyFromLocalFile: 顾名思义,从本地文件拷贝 /** * 使用Java API操作HDFS文件系统 * 关键点: * 1)create Configur ...

  8. djiango 虚拟环境与项目创建

    建立虚拟环境 一,查看有那些虚拟环境 :workon 二,创建虚拟环境:mkvirtualenv -p/usr/bin/python3 django(p后面是路径) 三,进入虚拟环境:workon d ...

  9. 简介C#读取XML的方式(转)

    在程序中访问进而操作XML文件一般有两种模型,分别是使用DOM(文档对象模型)和流模型,使用DOM的好处在于它允许编辑和更新XML文档,可以随机访问文档中的数据,可以使用XPath查询. XML作用 ...

  10. Django中ORM实际应用

    1. Django中ORM的使用 1. 手动新建一个数据库 2. 告诉Django连接哪个数据库 settings.py里面配置数据库连接信息: # 数据库相关的配置项 DATABASES = { ' ...