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. kettle在centos7下部署分布式集群

    首先安装三台centos7 ,分别配置好静态ip    ssh免密码登录     关闭防火墙 具体步骤这里不多说了 关于centos7配置静态ip大家可以参考:https://www.cnblogs. ...

  2. [java 2019-04-09] 代码生成word文档中的表格嵌套问题

    public static void createContent3(Date adtStart, Date adtEnd, Map<String, Object> aMap,Map< ...

  3. HDU1671 Phone List

    Phone List Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Probl ...

  4. java 继承内存分配

    今天,复习的是继承的内存分配.我们知道,Java中内存可以初略分为堆.栈.方法区. package sort; class Person{ public int age; public String  ...

  5. 【HDFS API编程】jUnit封装-改写创建文件夹

    首先:什么是jUnit  回顾: https://www.cnblogs.com/Liuyt-61/p/10374732.html 上一节我们知道: /** * 使用Java API操作HDFS文件系 ...

  6. python_装饰器

    越来越觉得写一点技术博客是有多么重要了,明日复明日,现在就开始写吧! 1. 普通装饰器 装饰器的写法是一种语法糖,装饰器也还是一个函数而已,它接收一个函数对象作为参数,并返回一个新函数,主要是拓展原函 ...

  7. KVM虚拟化环境安装随笔

    1. KVM由来 KVM:Kernel-based Virtual Machine 2.软件说明 libvirt  作用:虚拟机的管理软件 virt   virt-install virt-clone ...

  8. SQL SERVER 2019新功能

    1.错误代码行 BEGIN TRY    SELECT 1/0END TRYBEGIN CATCH    THROW END CATCH2.二级制截断列名值 chose语法

  9. jakarta-taglibs-standard-1.1.0查找下载

  10. Never Wait for Weights(带权并查集+路径压缩)

    题目链接:http://acm.sdibt.edu.cn/vjudge/contest/view.action?cid=2209#problem/F !a b w 表示b比a大w ?  a b  输出 ...