不止 JDK7 的 HashMap ,JDK8 的 ConcurrentHashMap 也会造成 CPU 100%?原因与解决~

JDK8 的 ConcurrentHashMap 有 Bug? JDK8 的 ConcurrentHsahMap 怎么可能会有 bug?的确我们 JDK8 中存在这个 Bug,但是这个 Bug 不影响使用,并且在 JDK12 中对其进行了修复在这个 JDK 的 Bug 收集论坛上,我们可以看到这个报告指出的是在 ConcurrentHsahMap 进行 addCount() 的时候会有问题存在,我们先看一下具体在 JDK8 和 JDK12 中该方法的体现JDK8 复制代码JDK12 复制代码我们仔细对比一下代码,就可以发现在 JDK12 中去除了 (sc >>> 阅读详情

点击上方“芋道源码”,选择“设为星标”

做积极的人,而不是积极废人!

源码精品专栏

 

来源:http://t.cn/EIjljJC

  • 现象

  • 原因

  • 解决


现象

大家可能都听过JDK7中的HashMap在多线程环境下可能造成CPU 100%的现象,这个由于在扩容的时候put时产生了死链,由此会在get时造成了CPU 100%。这个问题在JDK8中的HashMap获得了解决。其实JDK7中的HashMap在多线程环境下不止只有CPU 100%这一共怪异现象,它还可能造成插入的数据丢失,有兴趣的读者可以自行了解下。

对于HashMap多线程的问题,我们通常会这么反问:HashMap设计上就不是多线程安全的,何必要去在多线程环境下用呢?的确如此,我们不会傻到显式的在多线程环境下调用,但是又可能在你所关注的视角范围外是多线程的,其隐式地让HashMap置于多线程环境下了,这个又难以一下子察觉到。再者,对于HashMap多线程的问题,我们很多时候推荐使用ConcurrentHashMap来代替HashMap应用于多线程的环境,很不巧的是ConcurrentHashMap也有可能会造成CPU 100%的异常现象。这个怪异现象存在于JDK8的ConcurrentHashMap中,在JDK9中已经得到修复,可以参见:https://bugs.openjdk.java.net/browse/JDK-8062841

什么情况下JDK8的ConcurrentHashMap会出现这个Bug呢?首先我们来运行一下这段代码:

map.computeIfAbsent("AaAa",
        (String key) -> {
            map.put("BBBB", "value");
            return "value";
        });

你会惊奇的发现这个程序一直处于Running状态,我们通过top -Hp [pid]命令查看到其中一个线程的CPU使用率接近100%,参考下图:

可以看到pid为31417的东东,我们再通过jstack -l [pid]命令查看到对应的线程为:

注意将nid=0x7ab9的16进制转为10进制就是31417。可以看到问题是发生在了computeIfAbsent方法中,我们将示例中的程序换成下面这段程序也会同样出现CPU 100%的Bug:

static Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<>();

public static void main(String[] args) {
    System.out.println("Fibonacci result for 20 is" + fibonacci(20));
}

static int fibonacci(int i) {
    if (i == 0)
        return i;

    if (i == 1)
        return 1;

    return concurrentMap.computeIfAbsent(i, (key) -> {
        System.out.println("Value is " + key);
        return fibonacci(i - 2) + fibonacci(i - 1);
    });
}

至于为什么会发生这个BUG,答案就在ConcurrentHashMap中的computeIfAbsent方法中,自己去捞吧,嘿嘿。

原因

map.computeIfAbsent(key1, mappingFunction)

如果当前key1-hash对应的tab位(可以理解为槽)刚好是空的,在计算mappingFunction之前会

  • step1: 先往对应位置放一个ReservationNode占位

  • step2: 然后计算mappingFunction的值value,

  • step3: 再将value组装成最终NODE, 把占位的ReservationNode换成最终NODE;

这时如果:
mappingFunction 中用到了 当前map的computeIfAbsent方法, 很不巧 key2-hash的槽为和key1的是同一个,
因为key1已经在槽中放入了占位节点, 在处理key2时候for循环的所以处理条件都不符合 程序进入了死循环

但是如果:

key2-hash的槽位和key1的不一样, 是不会发生死循环

多线程问题:

因为ConcurrentHashMap在处理上述step1-step3是同步的, 而且在处理时候会同步获取的值, 所以是不存在线程不安全的, 纯粹是当前线程死循环

Thread1 通过cas 在槽x放了个ReservationNode(RN1), 然后假设mappingFunction执行的很慢

Thread2 在槽x和Thread竞争, cas失败没有抢到占位符; 进行下一轮for循环, 这是因为槽x中已经被放置了RN1, 所以Thread2获取到这个RN1,在执行synchronized(RN1) 时候被thread1block住

code sample:

public static void main(String[] args) throws IOException {
    ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
    //caseA: dead loop
//        map.computeIfAbsent("AA", key -> map.computeIfAbsent("BB", k->"bb"));

    //caseB: block, but no dead loop
    new Thread(()->map.computeIfAbsent("AA", key -> waitAndGet())).start();

    new Thread(()->{
        try {
            TimeUnit.SECONDS.sleep(3);  //delay 1 second
        } catch (InterruptedException e) {}
        map.computeIfAbsent("BB", key-> "bb");
    }).start();

}

private static String waitAndGet(){
   try {
       TimeUnit.SECONDS.sleep(20);
   } catch (InterruptedException e) {
   }
   return "AAA";
}

解决

怎么规避这个问题呢?只要不在递归中使用computeIfAbsent方法就好啦,或者降级用可爱的分段锁,或者升级JDK9~



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:

已在知识星球更新源码解析如下:

如果你喜欢这篇文章,喜欢,转发。

生活很美好,明天见(。・ω・。)ノ♡

不只是CPU主频:深入解析英飞凌Aurix TC3xx各外设时钟树(CAN、以太网、Flash时序如何配置) 本文深入解析英飞凌Aurix TC3xx系列微控制器的外设时钟树配置,涵盖CAN、以太网和Flash时序的优化方法。通过详细分析时钟系统架构、关键外设配置及多外设协同设计策略,帮助工程师实现高性能汽车电子控制单元(ECU)开发。特别关注系统时钟的灵活配置与EMC优化技巧,提升系统实时性和可靠性。 阅读详情

相关推荐

Linux实战:rMATS可变剪切分析从零到一,避坑指南与结果深度解析

本文详细介绍了在Linux系统上从零开始部署rMATS进行可变剪切分析的完整流程,包括环境搭建、安装避坑指南、参数优化及结果解读。特别针对gfortran依赖、数学库配置等常见问题提供解决方案,并深入解析JC与JCEC结果的差异及可视化技巧,帮助生物信息学研究者高效完成可变剪切分析。

weixin_30394669的博客 411

记一个内存泄漏问题:Query Plan Cache BoundedConcurrentHashMap占用堆内存

原因是hibernate会缓存sql语句以减少重复编译,便于直接命中提高效率。这个缓存默认最大值为2G,且在使用in时,只要in后面的参数有任何一个不一样的,就会视为不同的语句而保存下来。 解决这个问题可以将缓存最大值改小。 我看大家都是这样写的: spring: jpa: properties: hibernate: query: plan_cache_max_size: 64 plan_parameter_metadata_

yogima的博客 4505

WPT无线电能传输公式推导、编程计算、仿真验证全过程

主要分为5大模块,分别为无补偿,SS,SP,PP,PS,每个模块对应有公式推导,matlab编程计算相关参数以及将计算参数和仿真验证观察是否对应。采用matlab编程计算参数以及simulink仿真验证

zzhao2580的博客 7761

JVM成长之路,记录一次内存溢出导致频繁FGC的问题排查及解决

现象: 现象截图: 内存: 命令:jmap -heap30069 GC截图: FGC 次数 19529 次!!!何等的恐怖!!!!! 命令: jstat -gcutil 30069 1000 现象描述: Node模块启动后收到请求却未能响应。 一直在频繁的FGC。新生代内

coderlaw's study 1万+

Unity Android构建报错:compileSdk 35兼容性五层定位法

Android构建中的compileSdk是编译时API目标版本,其升级并非孤立操作,而是触发JDK、Android Gradle Plugin(AGP)、NDK、targetSdk与Unity引擎版本之间强耦合校验的起点。当出现'using a newer Android Gradle plugin to use compileSdk = 35'类报错,本质是代际技术栈错配——例如AGP 7.1.3不识别compileSdk 35所需Java 17语法,或NDK 21与AGP 7.4的ABI规范冲突。该问

weixin_32496547的博客 569

TIBCO Spotfire 入门指南

数据分析工具 - TIBCO Spotfire 入门指南(一) 文章目录数据分析工具 - TIBCO Spotfire 入门指南(一)前言一、Sportfire 是什么?二、使用Sportfire 创建一个简单的数据分析模型1.设置分析(加载数据)2.编辑数据源3.创建图表3.1 创建交叉表3.2 创建饼图3.3 创建树形图3.4 创建散点图3.5 创建自定义筛选器总结 前言 通过对本指南的学习,你可以了解到如何开始用TIBCO Sportfire分析数据,并能详细了解您可以做什么以及怎么做 一、Spo

kaiakai111的博客 1万+

HashMap与ConcurrentHashMap的内存占用与get操作性能比较

一、测试背景         项目中需要提供一个单机计算视频相似度的服务,计算的方式是对视频标题进行分词,提取关键词,然后通过word2vec的方式对关键词进行embedding,最后通过向量累加得到视频的词向量,然后通过某种相似度算法(比如欧式距离)得到视频相似度。这个服务要求5ms返回,可行性预研阶段需要估算响应时间能否达到要求,需要多少台机器支撑每天50亿的请求量。这里面有两个关键内容需要

优酷土豆大数据团队的博客 1万+

逆向实战:手把手教你分析Google DroidGuard虚拟机的加密与反调试(附Python脚本)

本文深入解析Google DroidGuard虚拟机的加密机制与反调试策略,提供从环境配置到逆向分析的完整实战指南。通过IDA Pro、Frida等工具链,详细剖析DroidGuard的信号拦截、内存校验等保护机制,并附Python脚本实现自动化分析,助力Android安全研究人员突破GMS核心组件的安全防护。

weixin_32428571的博客 313

JDK8的ConcurrentHashMap也会造成CPU 100%

转载:不止 JDK7 的 HashMap ,JDK8 的 ConcurrentHashMap 也会造成 CPU 100%?原因与解决~ 现象 大家可能都听过JDK7中的HashMap在多线程环境下可能造成CPU 100%的现象,这个由于在扩容的时候put时产生了死链,由此会在get时造成了CPU 100%。这个问题在JDK8中的HashMap获得了解决。其实JDK7中的HashMap...

351

手把手教你学Simulink实例--基于Simulink的电动汽车无线充电效率优化仿真

​市场规模:2025年全球无线充电市场规模将达$27亿(MarketsandMarkets数据)​技术标准Qi v1.3(最高150W)WiTricity(200W动态功率)ISO 17163(道路车辆无线充电)​节能效果:每kWh充电减少120W损耗(按每天8小时)​成本分析线圈成本:35vs58(传统方案)效率提升带来的续航增加:15km/100km。

xiaoheshang_123的博客 346

华为OD机试真题2025双机位A卷 Java&Go 实现【采购订单】

在一个采购系统中,采购申请(PR)需要经过审批后才能生成采购订单(PO)。每个PR包含商品的单价(假设相同商品的单价一定是一样的)及数量信息。系统要求对商品进行分类处理:单价高于100元的商品需要单独处理,单价低于或等于100元的相同商品可以合并到同一采购订单PO中。针对单价低于100的小额订单,如果量大可以打折购买。

MISAYAONE的博客 1261

不止JDK7的HashMap,JDK8的ConcurrentHashMap也会造成CPU 100%

点击上方“Java后端技术”,选择“置顶或者星标”每天带你看高清大图哦!作者:朱小厮公众号:朱小厮的博客(ID:hiddenkafka)大家可能都听过JDK7中的Hash...

Java后端技术 329

【YOLO11改进 - 注意力机制】SCSA通过结合空间注意力和通道注意力,提高各种下游视觉任务的性能。

【YOLO11改进 - 注意力机制】SCSA通过结合空间注意力和通道注意力,提高各种下游视觉任务的性能。

专注于图像领域,主要研究内容包括计算机视觉和深度学习,特别是在图像分类、目标检测和图像生成等方面有深入的研究和实践经验。 478

HashMap在多线程环境下导致CPU 100%的深度解析与解决方案

摘要 本文深入分析了Java中HashMap在并发环境下导致CPU使用率100%的问题。通过真实案例展示了多线程访问HashMap时出现的系统瘫痪现象,并详细解析了JDK 1.7中因头插法导致的扩容死循环问题。虽然JDK 1.8改用尾插法解决了死循环,但HashMap仍存在数据丢失、size计算错误等线程安全问题。文章还介绍了哈希碰撞攻击风险,并提供了使用JVM工具诊断问题的具体方法,包括定位高CPU线程、分析线程栈和堆内存等诊断步骤,为开发人员提供了全面的问题分析和解决方案。

cx1050306424的博客 1127

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

ConcurrentHashMap也有可能会造成CPU 100%的异常现象

1、JDK7 A、JDK7中的HashMap在多线程环境下不止只有CPU 100%这一共怪异现象 B、HashMap在多线程环境下它还可能造成插入的数据丢失 JDK7中的HashMap在多线程环境下可能造成CPU 100%的现象,这个由于在扩容的时候put时产生了死循环,由此会在get时造成了CPU 100%。所以在并发情况下不能使用HashMap,这个问题在JDK8中的HashMap获得了解决。...

weixin_43870079的博客 1782

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

多线程环境下HashMap导致CPU100%

引言 昨天早上线上系统开始作业了一段时间以后,突然收到服务器报警,服务器CPU持续占用100%,导致线上系统不能正常使用,我登录服务器top了一下,发现java进程占用cpu400%, 由于前天晚上上线了一些新的功能,所以我分析应该是某处代码出现了死循环导致,于是根据前面解决性能问题的经验来搞一下。具体流程参考我前面的博文《快速定位线上CPU100%问题》。 排查结果: 快速找到具体的代码,那么问题就可以很好的解决了,看一下具体代码 public static Map<String,

Jack 架构师之路 8356

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

HashMap和ConcurrentHashMap对比源码分析

为了能让存储更加高效,尽量的避免key冲突,让数据尽量均匀的进行分布,因此采用了hash值计算的方式,hash值的范围为-2147483648 到 2147483647。因此当负载因子为1.0时,在进行扩容时,会出现更多的hash冲突,可能导致链表长度或红黑树高度会变得更长或更高,导致查询效率的降低。长度为8的时候,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。

XING的博客 1166

那行static HashMap,是如何把服务器CPU干到100%的?

一行static HashMap引发线上服务CPU 100%雪崩,这绝非危言耸听。当多线程扩容的“死亡循环”幽灵浮现,你以为换成ConcurrentHashMap就万事大吉了?天真!从JDK 7的分段锁到JDK 8的CAS+synchronized,它的进化远超你想象。本文将带你亲历一次从“线上爆雷”到“架构封神”的Map认知升级,不仅剖析源码,更揭示其在Spring、Dubbo中的核心地位,以及用Map优雅重构业务逻辑的架构级玩法。

专注生产事故复盘。这里没有空洞理论,只有从P0级告警中打捞出的源码级真相和企业级填坑指南,带你从“踩坑”到“封神”。 952

阿里二面:听说过 HashMap 会导致CPU飙升100%吗?

一、问题描述经常有些面试官会问,关于这个问题,很多年前,在淘宝内网里就有很多的程序员发过这种帖子说,并且这个事发生了很多次。虽然 Java 官方明确表示,在多线程环境下不推荐使用 HashMap,但是对于这种问题,小编其实也比较意外,如果不是深入的去了解 HashMap,都不知道有这样的问题。为什么会产生死循环呢?下面我们来还原一下问题的经过。

lingqu+wx bjmsb06 1138

HashMap / ConcurrentHashMap 深入拆解:JDK7 vs JDK8,从“扩容坑”到“高并发实现”

摘要:本文深入解析HashMap与ConcurrentHashMap的核心原理。HashMap通过数组+链表/红黑树实现O(1)查找,关键设计包括2的幂容量、扰动函数和扩容优化。JDK8改进包括红黑树、尾插法和按位拆分扩容。ConcurrentHashMap在JDK7采用分段锁,JDK8升级为CAS+桶锁+协作扩容机制,显著提升并发性能。文章还对比了两者差异,指出HashMap线程不安全问题及使用注意事项,并总结了ConcurrentHashMap如何通过细粒度锁和协作机制实现高效并发访问。

haomingplus的博客 1014
上一篇: 手写一个迷你版的 Tomcat 喵
下一篇: 8 张图,看你是否理解 Java
公众号-芋道源码
博客等级 码龄10年 1636粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值