由JDK bug引发的线上OOM

由JDK bug引发的线上OOM

最近生产环境的一个应用忽然发生了OOM,还好是业务低峰期,没有导致什么严重问题,下面记录下本次排查的过程;

故障临时处理

在某天下午,正在愉快的写代码时,忽然看到业务反馈支付服务不能用的消息,因为最近没有发布,所以感觉不会是什么大事,十有八九是网络波动啥的,毕竟之前遇到过好多次,那剩下的就是找证据了,先看看日志,有没有报错(暂时还未接告警,所以要人肉看),结果不看不要紧,一看吓一跳,日志密密麻麻全都是OOM报错

微信公众号:代码深度研究院

幸好作者这百年Java开发经验不是白给的,反手就是一个重启服务,虽然看起来只是一个简单的重启,但是操作起来可并不简单,里边的道道还是很多的,重启的时候要注意留一台作为现场保护起来,同时给这台保留现场的实例的流量摘除掉,然后给其他实例重启起来,这样用户就能正常使用了;

本来以为事情到这里就结束了,但是就当我准备继续下一步的时候,我发现重启的早的那台机器内存已经又直线上升上来了;

微信公众号:代码深度研究院

不过这也难不倒我,既然重启后服务内存又开始飙升,说明肯定是定时任务、批处理之类的触发了,查看了下日志,果然是有大批的定时任务在执行,将定时任务暂停后发现就好了,下面开始对现场进行分析;

现场处理

登录到我们保留的现场机器上,使用下面的命令执行堆dump,方便我们后续分析:

# 安装gdb,如果机器上有就无需安装
yum install -y gdb

# 设置不限制core dump大小
ulimit -c unlimited

# 生成core dump,文件名叫core,也可以自己起名,100是目标Java进程pid,这个需要根据实际的来,命令执行完毕后会生成一个core.100的core dump
gcore 100 -o core

有的同学可能看到这里就开始迷糊了,Java堆dump不是用jmap命令吗,上边的命令跟jmap也没什么关系呀,我们这里之所以用gcore而不是jmap来dump,主要是因为在OOM时,通常JVM已经无法正常使用jmap来dump了(针对本次排查就是这种情况),如果你一定要使用jmap来操作,那么他会报错,无法进行堆dump,同时会在错误信息中告诉我们可以尝试使用jmap -F参数来进行堆dump,但是加上这个参数后,你会发现噩梦开始了,因为此时虽然能正常进行dump,但是速度可以说是惨不忍睹,4G的堆dump时间要按小时算,本质上是因为当我们使用jmap -F来进行堆dump的时候实际上底层使用了ptrace来dump(使用ptrace读取目标进程内存然后写出到文件),由于ptrace一次最多只能读取4字节(32位机器),所以导致他的速度也极其的慢; 而gcore生成速度相对于正常jmap来说也是比较快的,对于jmap -F就更快了; 所以,基于以上几点,我们选择了使用gcore来进行堆dump;

当我们使用gcoredump完后,因为最终还是需要使用Java系的工具进行内存分析,所以还是要将core dump转换为Java的堆dump,此时我们就可以执行以下命令来转换了:

注意,core dump完成后就可以先重启服务了,重启完服务再进行下面的步骤;

# 生成堆dump
jmap -dump:format=b,file=heap.hprof `which java` core.100

堆dump生成完毕后,将其下载下来,然后导入eclipse Memory Analyzer(MAT)开始分析,发现大量org.bouncycastle.jce.provider.BouncyCastleProvider实例被javax.crypto.JceSecurity类的verificationResults这个静态字段持有,下面就可以开始源码分析了;

具体怎么分析出是这个地方内存泄漏这里不做单独说明了,可以自行查询官方使用文档,后续也会考虑单独出一期分析方法的文章;

问题分析

查看javax.crypto.JceSecurity的源码,发现verificationResults是一个map,同时只在getVerificationResult这个方法中被放入了数据,源码如下:

微信公众号:代码深度研究院

经过结合我们的业务代码分析,发现是我们调用了javax.crypto.Cipher.getInstance(java.lang.String, java.security.Provider)这个方法,这个方法调用了javax.crypto.JceSecurity.getVerificationResult方法;getVerificationResult这个方法比较简单,就是对我们提供的java.security.Provider所在的jar进行签名校验,校验完毕后将我们提供的Provider作为key、校验结果作为value放入verificationResults这个map缓存,下次就不用校验了,但是这里有个问题,就是这个缓存没有任何清理机制,也就意味着我们如果频繁调用javax.crypto.Cipher.getInstance(java.lang.String, java.security.Provider)来获取AES实例的话,就是可能会导致内存泄漏的,经过我们验证,也确实是这样,可以使用以下代码复现:

注意指定jvm参数: -Xmx128m

import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * @author JoeKerouac
 * @date 2023-08-24 12:50
 */
public class Test {

    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException {
        for (int i = 0; i < 500; i++) {
            Cipher c = Cipher.getInstance("AES", new BouncyCastleProvider());
        }
    }

}

问题解决

既然问题定位到了,那解决起来就比较简单了,我们可以把Provider实例全局共享,或者使用Provider的名字来获取AES实例,这样不去反复创建Provider而是使用同一个Provider,在JceSecurity中自然也不会内存泄漏,代码如下:

至于作者为什么不使用单例?那是因为AES并不是线程安全的,无法全局共享,当然,可以使用单例然后自行控制并发,或者使用对象池技术、ThreadLocal等来解决;

import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * @author JoeKerouac
 * @date 2023-08-24 12:50
 */
public class Test {

    private static BouncyCastleProvider provider = new BouncyCastleProvider();

    public static void main(String[] args)
        throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
        // 使用全局共享的provider
        Cipher cipher = plan1();

        // 使用provider的名字获取AES实例,其实本质上也是全局共享了provider
        // 注意,如果要使用provider的名字获取AES实例,要先注册
        Security.addProvider(new BouncyCastleProvider());
        cipher = plan2();
    }

    public static Cipher plan1() throws NoSuchAlgorithmException, NoSuchPaddingException {
        return Cipher.getInstance("AES", provider);
    }

    public static Cipher plan2() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
        return Cipher.getInstance("AES", "BC");
    }

}

问题溯源

这应该是一个比较容易发现的问题,既然这样,那有没有人提出这个问题呢,想到这里,作者打开了Google,经过一番搜索后(其实很容易就能搜到,只需要搜索关键字javax.crypto.JceSecurity#getVerificationResult即可),发现确实有人给jdk提了这个bug,而且也给出了解决方案,代码已经合并到了master,不过截至发文时,在作者使用的eclipse jdk(Temurin)中,jdk8jdk17这两个版本的最新发布中(2023-07-25发布)仍然存在该问题,并未修复,所以如果遇到该问题,还是需要使用上边的解决方案来处理;

官方bug记录:https://bugs.openjdk.org/browse/JDK-8168469

至此,我们的问题已经解决了,通过本篇文章,你应该大概知道了线上发生OOM时的处理流程了,以后碰到类似问题可以按照本流程来直接套用,至少大多数场景下都是可以的;

联系我

  • 作者微信:JoeKerouac
  • 微信公众号(文章会第一时间更新到公众号,如果搜不出来可能是改名字了,加微信即可=_=|):代码深度研究院
  • GitHub:https://github.com/JoeKerouac

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/88597.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

湘潭大学 湘大 XTU OJ 1055 整数分类 题解(非常详细)

链接 整数分类 题目 Description 按照下面方法对整数x进行分类&#xff1a;如果x是一个个位数&#xff0c;则x属于x类&#xff1b;否则将x的各位上的数码累加&#xff0c;得到一个新的x&#xff0c;依次迭代&#xff0c;可以得到x的所属类。比如说24&#xff0c;246&#…

XXX程序 详细说明

用于记录理解PC程序的程序逻辑 1、程序的作用 根据原作者的说明&#xff08;文件说明.txt&#xff09;&#xff0c;该程序 (PC.py) 的主要作用是提取某一个文件夹中的某个设备 (通过config中的信息看出来是Ag_T_8) 产生的日志文件&#xff0c;然后提取其中某些需要的数据&…

MyCAT命令行监控

9066端口 &#xff0c;用mysql命令行连接 Mysql –utest –ptest –P9066 show help 可显示所有相关管理命令 显示后端物理库连接信息&#xff0c;包括当前连接数&#xff0c;端口 Show backend Show connection 显示当前前端客户端连接情况&#xff0c;已经网络流量信息、…

学习中ChatGPT的17种用法

ChatGPT本质上是一个聊天工具&#xff0c;旧金山的人工智能企业OpenAI于2022年11月正式推出ChatGPT。那么&#xff0c;ChatGPT与其他人工智能产品相比有什么特殊呢&#xff1f; 它除了可以回答结构性的问题&#xff0c;例如语法修正、翻译和查找答案之外。最关键的是它能够去解…

mysql--数据库的操作

数据库&#xff0c;是数据存储的最大单元。 1 创建数据库 create database mydatabase; 每次创建数据库的时候&#xff0c;都会多一个文件夹&#xff0c;关系型数据库是存储在磁盘当中的&#xff0c;所以这时候可以查看新建的数据库 2 指定字符集 MySQL中的字符集转换过程 制…

工程师是怎样对待开源

工程师如何对待开源 本文是笔者作为一个在知名科技企业内从事开源相关工作超过 20 年的工程师&#xff0c;亲身经历或者亲眼目睹很多工程师对待开源软件的优秀实践&#xff0c;也看到了很多 Bad Cases&#xff0c;所以想把自己的一些心得体会写在这里&#xff0c;供工程师进行…

三维模型OBJ格式轻量化压缩处理的数据质量提升方法分析

三维模型OBJ格式轻量化压缩处理的数据质量提升方法分析 在三维模型的OBJ格式轻量化压缩处理过程中&#xff0c;除了减小文件大小和提高加载速度之外&#xff0c;我们也需要考虑如何提升数据质量。以下是几种常见的方法&#xff1a; 1、优化顶点数据&#xff1a;顶点数据是三维…

学习心得04:CUDA

2018年的时候&#xff0c;看过同事使用CUDA。因为工作忙&#xff0c;所以也没请教。 近来买了本入门的CUDA书&#xff0c;学习了一番。有两个心得&#xff1a; 工作拆分。 CUDA是并行计算&#xff0c;也就是大量重复的可拆分的计算。数组最符合这个要求。简单点就是把数组外面…

战略企业家派:企业家愿景形成的过程

战略企业家派&#xff1a;战略的是企业家愿景形成的过程【安志强趣讲267期】 趣讲大白话&#xff1a;企业家才是关键因素 **************************** 战略企业家派的代表是熊彼特 他认为企业家的职责在创新 只有创新才能赢得更多利润 创新是新产品或新生产方式的各种组合 提…

【动手学深度学习】--20.目标检测和边界框

文章目录 目标检测和边界框1.目标检测2.边界框 目标检测和边界框 学习视频&#xff1a;物体检测和数据集【动手学深度学习v2】 官方笔记&#xff1a;目标检测和边界框 在图像分类任务中&#xff0c;我们假设图像中只有一个主要物体对象&#xff0c;我们只关注如何识别其类别…

CTFshow——web入门——反序列化web254-web278 详细Writeup

前言 在做题之前先简要总结一下知识点 private变量会被序列化为&#xff1a;\x00类名\x00变量名 protected变量会被序列化为: \x00\*\x00变量名 public变量会被序列化为&#xff1a;变量名__sleep() &#xff1a;//在对象被序列化之前运行__wakeup() //将在反序列化之后立即…

ChatGPT提示与技巧分享:如何作出更好的提示2023年8月

​对ChatGPT的一些酷炫技巧感兴趣吗?这里提供了一些可以帮助你充分利用ChatGPT&#xff0c;成为AI工具专家的技巧。 毫无疑问&#xff0c;ChatGPT是目前最广泛使用的人工智能工具之一。它不仅毫不留情地取代了一些特定领域常用的软件小工具&#xff08;如智能对联、经典语录生…

密码学学习笔记(二十):DSA签名与X.509证书

数字签名 下图是一个制作以及使用数字签名过程的通用模型。 假设Bob发送一条消息给Alice&#xff0c;尽管消息并不重要&#xff0c;也不需要保密&#xff0c;但他想让Alice知道消息确实是他本人发的。出于这个目的&#xff0c;Bob利用一个安全的散列函数&#xff0c;比如SHA-…

【C++初阶】list的常见使用操作

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

初探修模的三维模型OBJ格式轻量化压缩的遇到常见问题与处理方法

初探修模的三维模型OBJ格式轻量化压缩的遇到常见问题与处理方法 在对经过修模的三维模型进行OBJ格式轻量化压缩处理的过程中&#xff0c;可能会遇到一些常见问题。以下是一些常见问题以及相应的处理方法&#xff1a; 1、顶点丢失和形状变形&#xff1a;在减小顶点数量的过程中…

k8s 常用命令(三)

1、查看版本信息&#xff1a;kubectl version [rootmaster ~]# kubectl version [rootmaster ~]# kubectl version Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.3", GitCommit:"ca643a4d1f7bfe34773c74f7952…

前端学习记录~2023.8.10~JavaScript重难点实例精讲~第6章 Ajax

第 6 章 Ajax 前言6.1 Ajax的基本原理及执行过程6.1.1 XMLHttpRequest对象&#xff08;1&#xff09;XMLHttpRequest对象的函数&#xff08;2&#xff09;XMLHttpRequest对象的属性 6.1.2 XMLHttpRequest对象生命周期&#xff08;1&#xff09;创建XMLHttpRequest对象&#xff…

Django基础3——视图函数

文章目录 一、基本了解1.1 Django内置函数1.2 http请求流程 二、HttpRequest对象&#xff08;接受客户端请求&#xff09;2.1 常用属性2.2 常用方法2.3 服务端接收URL参数2.4 QueryDict对象2.5 案例2.5.1 表单GET提交2.5.2 表单POST提交2.5.3 上传文件 三、HttpResponse对象&am…

OnePlus Open可折叠手机:规格、价格、发布日期等详细信息汇总!

我们知道OnePlus可折叠手机即将问世,无论它是否被命名为OnePlus Open。我们迫不及待地想让它到来,为该公司再添一根弦,为最好的可折叠手机增添一个新的竞争对手。 OnePlus以前没有生产过任何可折叠产品,但它确实拥有合作伙伴公司Oppo的丰富知识,并可以向三星、摩托罗拉和…

【C语言】基础知识杂记(整理自用)

前言 之前一直在学新知识&#xff0c;最近打算复习一下之前学的&#xff0c;所以写了这篇文章&#xff0c;记录一下不熟练的知识点&#xff0c;自用&#xff0c;对大家帮助可能不是很大。 double类型与float类型 编译器默认7.0为double类型 在数据后加一个f&#xff0c;编译…