不正当使用HashMap导致cpu 100%的问题追究

因最近hashmap误用引起的死循环又发生了一些案例,左耳朵浩子写了一篇blog 疫苗:Java HashMap的死循环,看了一下,大家的分析如出一辙。这篇blog也是好几年前写的了,之前在平台技术部的博客上贴过,随着组织结构的调整,那个博客可能不再维护,把这篇文章在这儿也保存一下。

李鹏同学在blog里写了篇关于HashMap死锁模拟的文章: http://blog.csdn.net/madding/archive/2010/08/25/5838477.aspx 做个纠正,那个不是死锁问题,而是死循环。

这个问题,我们以前讨论过。 校长之前的博客和淘宝的毕玄的《分布式Java应用:基础与实践》一书中都提到过 velocity导致cpu 100% 的bug,起因是HashMap的使用不当所致。

在之前的邮件列表里,校长提出过这个问题,当时我没仔细看,不清楚这个问题究竟是对 HashMap的误用,还是HashMap的潜在问题, 当时感觉不太可能是HashMap自身的问题,否则问题大了。应该是属于在并发的场景下错误的 使用了HashMap。昨天看了李鹏的blog后,觉得这个事情还是应该搞清楚一下;虽然我推测是链表形成闭环,但 没有去证明过。从网上找了一下: http://blog.csdn.net/autoinspired/archive/2008/07/16/2662290.aspx 里面也有提到:

产生这个死循环的根源在于对一个未保护的共享变量 — 一个”HashMap”数据结构的操作。当在 所有操作的方法上加了”synchronized”后,一切恢复了正常。检查”HashMap”(Java SE 5.0)的源 码,我们发现有潜在的破坏其内部结构最终造成死循环的可能。在下面的代码中,如果我们使得 HashMap中的entries进入循环,那 么”e.next()”永远都不会为null。

不仅get()方法会这样,put()以及其他对外暴露的方法都会有这个风险,这算jvm的bug吗?应该说不是的,这个现象很早以前就报告出来了(详细见: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457)。Sun的工程师并不认为这 是bug,而是建议在这样的场景下应用”ConcurrentHashMap”,在构建可扩展的系统时应将这点 纳入规范中。

这篇翻译提到了对HashMap的误用,但它没有点破HashMap内部结构在什么样误用情况下怎么被 破坏的;我想要一个有力的场景来弄清楚。再从李鹏的blog来看,用了2个线程来put就模拟出来了,最后堆栈是在 transfer 方法上(该方法是数据扩容时将数据从旧容器转移到新容器)。 仔细分析了一下里面的代码,基本得出了原因,证明了我之前的推测。

假设扩容时的一个场景如下(右边的容器是一个长度 2 倍于当前容器的数组) 单线程情况。

我们分析数据转移的过程,主要是链表的转移。

执行过一次后的状态:

最终的结果:

两个线程并发情况下,扩容时可能会创建出 2 个新数组容器

顺利的话,最终转移完可能是这样的结果

但并发情况下,出现死循环的可能场景是什么呢? 还要详细的分析一下代码,下面的代码中重点在 do/while 循环结构中(完成链 表的转移)。

// 扩容操作,从一个数组转移到另一个数组
void transfer(Entry[] newTable) {
    Entry[] src = table;
    int newCapacity = newTable.length;
    for (int j = 0; j < src.length; j++) {
        Entry<K,V> e = src[j];
        if (e != null) {
            src[j] = null;
            do {
                Entry<K,V> next = e.next; //假设第一个线程执行到这里
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            } while (e != null); // 可能导致死循环
        }
    }
}

2 个线程并发情况下, 当线程 1 执行到上面第 9 行时,而线程 2 已经完成了一 轮 do/while 操作,那么它的状态如下图:
(上面的数组时线程 1 的,已经完成了链表数据的转移;下面的是线程 2 的,它 即将开始进行对链表数据的转移,此时它记录 E1 和 E2 的首位已经被线程 1 翻 转了)

后续的步骤如下:

1) 插入 E1 节点,E1 节点的 next 指向新容器索引位置上的值(null 或 entry)

2) 插入 E2 节点,E2 的 next 指向当前索引位置上的引用值 E1

3)因为 next 不为 null,链表继续移动,此时 2 节点之间形成了闭环。造成了 死循环。

上面只是一种情况,造成单线程死循环,双核 cpu 的话占用率是 50%,还有导致 100%的情况,应该也都是链表的闭环所致。

最终,这并不是 HashMap 的问题,是使用场景的不当,在并发情况下选择非线程 安全的容器是没有保障的。

时间: 2024-11-02 03:01:17

不正当使用HashMap导致cpu 100%的问题追究的相关文章

SQLSERVER 导致CPU 100%怎么解决!

问题描述 网站10分钟内同时在线3500人,导致CPU100%.服务器X3330CPU.4G内存是不是数据库结构的还是SQLSERVER的优化没做好?求助 解决方案 解决方案二:有可能,或者用多台sqlserver做cluster?解决方案三:引用1楼findcaiyzh的回复: 有可能,或者用多台sqlserver做cluster? UP解决方案四:换CPU!解决方案五:对sql语句优化减少cpu占用时长换个强大的cpu或者考虑多服务器集群模式集群我没用用过但集群可以解决这个问题解决方案六:用

oracle数据库I/O slave wait 导致CPU 100%解决办法

某客户的一个rac 节点CPU使用率为100%,导致整体系统运行缓慢.    通过mpstat命令检查发现系统CPU使用率为100%,系统的CPU idle几乎为0. 导致系统整体负载极高,如下: [oracle@ecdbs1 ~]$ mpstat 1 10 Linux 2.6.9-89.5AXS2largesmp (ecdbs1)   2014年10月04日   09时52分39秒  CPU   %user   %nice %system %iowait    %irq   %soft   %

php进程导致服务器cpu 100问题追查过程

前段时间,出现了一次服务器cpu 占用100的问题.以下为追查原因的过程.仅当抛砖引玉,欢迎拍砖.查看占用cpu高的进程 想找出占用cpu高的进程,用top命令就可以搞定. $top .....此处省略n多行... 10434 root 20 0 509m 174m 1528 R 99.7 0.5 8:42.43 php 5638 root 20 0 509m 174m 1528 R 99.1 0.5 9:12.35 php 16390 root 20 0 541m 182m 5244 R 98

服务器cpu 100问题追查过程

作者:沧龙   前段时间,出现了一次服务器cpu 占用100的问题.以下为追查原因的过程.仅当抛砖引玉,欢迎拍砖. 查看占用cpu高的进程 想找出占用cpu高的进程,用top命令就可以搞定. $top .....此处省略n多行... 10434 admin 20 0 509m 174m 1528 R 99.7 0.5 8:42.43 php 5638 admin 20 0 509m 174m 1528 R 99.1 0.5 9:12.35 php 16390 admin 20 0 541m 18

linux中php-cgi占用cpu 100%

zijidelu 网站日志目录/home/hosts_log 日志文件有对应的网站id zijidelu php-cgi日志目录 /usr/local/php_fcgi/logs php-fpm.log slow.log 查看日志文件基本上就可以解决问题了,我这边客户网站是被入侵 上传了一个php ddos文件,当然如果不是此问题可以如下查看是不是程序问题有时使用 file_get_contents函数也可能导致cpu 100%哦 在 php.ini 中,有一个参数 max_execution_

求救-网站服务器被CC攻击,导致cpu高达100%。请教高手解决方法!

问题描述 网站服务器被CC攻击,导致cpu高达100%.请教高手解决方法! 网站服务器被CC攻击,w3pw进程超过25%导致cpu高达100%.请教高手解决方法!

千万级记录的Discuz论坛导致MySQL CPU 100%的优化笔记_Mysql

发现此主机运行了几个 Discuz 的论坛程序, Discuz论坛的好几个表也存在着这个问题.于是顺手一并解决,cpu占用再次降下来了. 前几天,一位朋友通过这篇文章找到了我,说他就是运行最新的 discuz 版本,MySQL 占用 CPU 100%,导致系统假死,每天都要重启好几次,花了一个多月的时间一直没有解决,希望我帮忙一下.经过检查,他的这个论坛最重要的几个表中,目前 cdb_members 表,有记录 6.2 万:cdb_threads 表,有记录 11万:cdb_posts表,有记录

PHP-CGI 进程 CPU 100% 与 file

有时候,运行 Nginx.PHP-CGI(php-fpm) Web服务的 Linux 服务器,突然系统负载上升,使用 top 命令查看,很多 php-cgi 进程 CPU 使用率接近100%.后来,我通过跟踪发现,这类情况的出现,跟 PHP 的 file_get_contents() 函数有着密切的关系. 大.中型网站中,基于 HTTP 协议的 API 接口调用,是家常便饭.PHP 程序员们喜欢使用简单便捷的 file_get_contents("http://example.com/"

快速找出php中可能导致cpu飙升问题的代码行

用cpu接近100%时,你如何找到导致cpu飙升的原因?我的思路是,首先找到进程正在执行的代码行,从而确定可能有问题的代码段.然后,再仔细分析有问题的代码段,从而找出原因. 如果你的程序使用的是c.c++编写,那么你可以很容易的找到正在执行的代码行.但是,程序是php编写的,如何找到可能有问题的代码行呢?这个问题就是本文要解决的问题. 背景知识: 大家都知道php是一个解释性语言.用户编写的php代码会生成opcode,由解释器引擎去解释执行.在解释执行过程中,有一个全局变量包含了执行过 程中用