高级JAVA工程师手把手教你解决JVM奔溃实战(IPV6引起Java jvm奔溃服务死亡经验诱发JDK8BUG)

本文主要是介绍高级JAVA工程师手把手教你解决JVM奔溃实战(IPV6引起Java jvm奔溃服务死亡经验诱发JDK8BUG),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、事故现象描述

生产环境频繁宕机,登录服务器一看,JAVA进程不存在。查看程序日志,根本没有显示到报错信息,写JAVA日志的进程也是跟着奔溃,
刚开始面试这个现象确实有些不好定位的问题。
重启Java进程,一会又反复宕机。从现象理性的分析,重启之后好了一会,客户在操作过程中触发了什么,引起了JVM报错,导致进程直接崩掉,系统日志都没有来得及产生。
通常情况下,我们都按照JAVA的日志翻看来定位问题,这次是直接JVM直接奔溃,也就是进程崩溃了,JAVA日志都留下线索。
但是这种JVM奔溃也有相应的日志,日志一般是在JAVA包的同目录。
下面先看看JVM日志的线索

二、从JVM奔溃日志分析

程序包同目录发现了
报错如下:

在这里插入图片描述

**核心报错如下:**ava frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  java.net.Inet6AddressImpl.lookupAllHostAddr(Ljava/lang/String;)[Ljava/net/InetAddress;+0
j  java.net.InetAddress$2.lookupAllHostAddr(Ljava/lang/String;)[Ljava/net/InetAddress;+4
j  java.net.InetAddress.getAddressesFromNameService(Ljava/lang/String;Ljava/net/InetAddress;)[Ljava/net/InetAddress;+51
j  java.net.InetAddress.getAllByName0(Ljava/lang/String;Ljava/net/InetAddress;Z)[Ljava/net/InetAddress;+29
j  java.net.InetAddress.getAllByName(Ljava/lang/String;Ljava/net/InetAddress;)[Ljava/net/InetAddress;+383
J 26303 C1 java.net.InetSocketAddress.<init>(Ljava/lang/String;I)V (47 bytes) @ 0x00007f135def1a74 [0x00007f135def1840+0x234]
J 37068 C1 sun.net.NetworkClient.doConnect(Ljava/lang/String;I)Ljava/net/Socket; (176 bytes) @ 0x00007f135f9a1bcc [0x00007f135f9a1520+0x6ac]
J 37067 C1 sun.net.www.http.HttpClient.openServer(Ljava/lang/String;I)V (104 bytes) @ 0x00007f135f99ed64 [0x00007f135f99ec60+0x104]
J 34310 C1 sun.net.www.http.HttpClient.openServer()V (188 bytes) @ 0x00007f135f1b1a5c [0x00007f135f1b04a0+0x15bc]
J 34305 C1 sun.net.www.http.HttpClient.<init>(Ljava/net/URL;Ljava/net/Proxy;I)V (129 bytes) @ 0x00007f135f08e2c4 [0x00007f135f08dd80+0x544]
J 36951 C1 sun.net.www.http.HttpClient.New(Ljava/net/URL;Ljava/net/Proxy;IZLsun/net/www/protocol/http/HttpURLConnection;)Lsun/net/www/http/HttpClient; (340 bytes) @ 0x00007f135f92cd24 [0x00007f135f92abc0+0x2164]
J 34194 C1 sun.net.www.protocol.http.HttpURLConnection.plainConnect0()V (698 bytes) @ 0x00007f135f183044 [0x00007f135f180080+0x2fc4]
J 34981 C1 sun.net.www.protocol.http.HttpURLConnection.plainConnect()V (75 bytes) @ 0x00007f135bb05a84 [0x00007f135bb05620+0x464]
J 34980 C1 sun.net.www.protocol.http.HttpURLConnection.connect()V (24 bytes) @ 0x00007f135b4a6fc4 [0x00007f135b4a6e40+0x184]
j  sun.net.www.protocol.http.HttpURLConnection.followRedirect0(Ljava/lang/String;ILjava/net/URL;)Z+314
J 38549 C2 sun.net.www.protocol.http.HttpURLConnection.getInputStream0()Ljava/io/InputStream; (2023 bytes) @ 0x00007f135fda2fbc [0x00007f135fda1340+0x1c7c]
J 37499 C2 sun.net.www.protocol.http.HttpURLConnection.getInputStream()Ljava/io/InputStream; (56 bytes) @ 0x00007f13598d3214 [0x00007f13598d3160+0xb4]
j  

开始以为是一个定位到下载网络文件的方法报错
在这里插入图片描述

加上finally也不好使,加上线程锁也不好使。改用httpclient实现,还是反复奔溃,而且错误都差不多。
通过逻辑判断,判断出不管是httpclient实现还是原生实现,都是调用到JDK的基础包,我就怀疑是JDKBUG,环境用到的JDK8,反复升级到最新,降版本都不好使,这个问题困扰哥1晚上。

上面只是定位到一个HTTP请求的方法,问题是这个代码在其他生产环境没有问题,在自己电脑上也没问题,还是非常头疼,主要是不好重现,并且问题也不是很明朗。报错信息也看不出来啥。

二、获取Java的jstack的日志分析

解决思路:尝试用jstack命令看看jvm的线程日志,看看有没有发现
第一步:获取这个JAVA的操作系统进程号

ps -ef|grep java #获取Java的进程ID

在这里插入图片描述

获取到JAVA进程ID之后则需要跑JVM的jstack命令
如果该命令执行不了,则需要进到JAVA的BIN目录下执行

       java -verbose #定位的JDK的安装路径

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

cd usr/jdk1.8.0_251/bin/                  #进到java bin路径
./jstack 22937 > /home/rocket/nasen.txt   #进Java bin目录执行22937是上个章节取的JAVA进程ID

额外补充:

JVM的线程日志,是该进程下的所有线程的日志,这个日志线程的ID在日志里面呈现是2进制的,此处还得需要转换一下才能匹配的上。我举个实际的例子:
假如JAVA进程生产运行中,有某几个进程占用很大CPU。我们想知道那个线程占据了最多的CPU资源

top -Hp 2630 #2360是Java进程ID    查看一个进程下的线程占用CPU

找到这个进程下占CPU最多的线程ID,然后转换2进制去跟上述jstack 的日志匹配,才能精准的找到是那个线程的日志!

./jmap -dump:format=b,file=/home/nasen.dump 2630 #jmap命令是把当前内存dump下来分析那内存用的,需要专业的软件分析内存泄露。这个这个场景暂时没有用到。

三、定位核心报错信息分析

核心日志报错如下:
q在这里插入图片描述
其实定位这一步,其实还是比较模糊的,定位不准确,只能定位到 iNet6AddressImpl.lookupAllHostAddr的报错引起。
inet6代表IPV6,顺便还去网上科普一下IPV4与IPV6的区别。简单说IPV6是IPV4的升级版本,当IPV4耗尽了,再用升级IPV6,但是现在似乎国内还是主流是IPV4。带着个这个线索继续往下思考!

在这里插入图片描述

四、核心问题定位

到当前步骤,莫福尔摩斯的你已经大概率猜出来IPV6的似乎是产生问题的核心的点。并且已经确定问题应该是JVM存在的BUG,我们99%的应用场景根本不会产生,归根到底是因为我们99%的场景都是用的IPV4老版本。现在已经有了禁用IPV4的这个初步的想法。下面去查查国外的程序员网站果然有收获!!

定位到这句inet6语句的方法,通过查到国外大神的网站,翻译英语解释到,无法解析的DNS地址确实IP6会导致JVM奔溃这个BUG,解决的思路就是禁用IP6,方法 启动的时候 -Djava.net.preferIPv4Stack=true ,测试生产环境恢复正常。触发场景为当DNS无法解析并且这个地址请求不通的时候触发这个JDKBUG,Inet6AddressImpl.lookupAllHostAddr这个方法在碰到无法解析域名的时候,会导致所有线程死锁!
2个条件,第一个域名没有解析过,没有注册过,地址而绝对不通。在可能地址掺杂着重定向302

参考BUG地址:

https://bugzilla.zimbra.com/show_bug.cgi?id=68432

国外大神的英文描述:

You'll see that's running Inet6AddressImpl.lookupAllHostAddr. Because of a bug between Java and libc, this lookup can enter an infinite loop when a certain race condition occurs. This occurs infrequently, but can cause deadlocks where all threads of one type (such as LMTP threads) or even all JVM threads can end up blocked.
With java.net.preferIPv4Stack set to true, Java will not execute this code and the problem should be avoided.
Configuration
1. Java processes can be configured to prefer the IPv4 stack. The default is to prefer the IPv6 stack, so it requires a specified JVM argument to prefer IPv4:
-Djava.net.preferIPv4Stack=true
This would need to be added to your existing mailboxd_java_options. Your existing configuration may vary depending on your performance tuning [see http://wiki.zimbra.com/wiki/Performance_Tuning_Guidelines_for_Large_Deployments], so be careful to append this option to whatever is there currently:$ zmlocalconfig mailboxd_java_options
$ zmlocalconfig -e mailboxd_java_options="-server -Djava.awt.headless=true -Dsun.net.inetaddr.ttl=60 -XX:+UseConcMarkSweepGC -XX:NewRatio=2 -XX:PermSize=192m -XX:MaxPermSize=192m -XX:SoftRefLRUPolicyMSPerMB=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/zimbra/log -Djava.net.preferIPv4Stack=true"
For more reference background on this, please review here:
http://bugzilla.zimbra.com/show_bug.cgi?id=13161#c55
2. Configuring the OS to disable IPv6
Each OS may have unique recommendations for disabling IPv6. This article does not currently include all OS-level recommendations, but please do a web search and determine methods for disabling the IPv6 interfaces, modules, and stack for your OS of choice.

五、事故解决问题总结
作者是真实解决生产事故做一次真实案例,提供给JVM宕机网友们一个解决问题的思路与方法。我们工作中99%遇到的是从程序日志翻看分析的,这次似乎更深到JVM的日志分析。还得去参考一下国外程序员同行的加成最终才能解决,希望写的东西能帮到你,如果真的帮到你了,记得给我点赞。
作者本人简介:现任国内某大型软件公司大数据研发工程师、MySQL数据库DBA,软件架构师。直接参与设计国家级亿级别大数据项目。并维护真实企业级生产数据库300余个。紧急处理数据库生产事故上百起,挽回数据丢失所操作的灾难损失不计其数。

这篇关于高级JAVA工程师手把手教你解决JVM奔溃实战(IPV6引起Java jvm奔溃服务死亡经验诱发JDK8BUG)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/280183

相关文章

python安装whl包并解决依赖关系的实现

《python安装whl包并解决依赖关系的实现》本文主要介绍了python安装whl包并解决依赖关系的实现,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录一、什么是whl文件?二、我们为什么需要使用whl文件来安装python库?三、我们应该去哪儿下

MySQL安装时initializing database失败的问题解决

《MySQL安装时initializingdatabase失败的问题解决》本文主要介绍了MySQL安装时initializingdatabase失败的问题解决,文中通过图文介绍的非常详细,对大家的学... 目录问题页面:解决方法:问题页面:解决方法:1.勾选红框中的选项:2.将下图红框中全部改为英

Java集合中的List超详细讲解

《Java集合中的List超详细讲解》本文详细介绍了Java集合框架中的List接口,包括其在集合中的位置、继承体系、常用操作和代码示例,以及不同实现类(如ArrayList、LinkedList和V... 目录一,List的继承体系二,List的常用操作及代码示例1,创建List实例2,增加元素3,访问元

Java中将异步调用转为同步的五种实现方法

《Java中将异步调用转为同步的五种实现方法》本文介绍了将异步调用转为同步阻塞模式的五种方法:wait/notify、ReentrantLock+Condition、Future、CountDownL... 目录异步与同步的核心区别方法一:使用wait/notify + synchronized代码示例关键

Java 8 Stream filter流式过滤器详解

《Java8Streamfilter流式过滤器详解》本文介绍了Java8的StreamAPI中的filter方法,展示了如何使用lambda表达式根据条件过滤流式数据,通过实际代码示例,展示了f... 目录引言 一.Java 8 Stream 的过滤器(filter)二.Java 8 的 filter、fi

Java中实现订单超时自动取消功能(最新推荐)

《Java中实现订单超时自动取消功能(最新推荐)》本文介绍了Java中实现订单超时自动取消功能的几种方法,包括定时任务、JDK延迟队列、Redis过期监听、Redisson分布式延迟队列、Rocket... 目录1、定时任务2、JDK延迟队列 DelayQueue(1)定义实现Delayed接口的实体类 (

springboot的调度服务与异步服务使用详解

《springboot的调度服务与异步服务使用详解》本文主要介绍了Java的ScheduledExecutorService接口和SpringBoot中如何使用调度线程池,包括核心参数、创建方式、自定... 目录1.调度服务1.1.JDK之ScheduledExecutorService1.2.spring

将java程序打包成可执行文件的实现方式

《将java程序打包成可执行文件的实现方式》本文介绍了将Java程序打包成可执行文件的三种方法:手动打包(将编译后的代码及JRE运行环境一起打包),使用第三方打包工具(如Launch4j)和JDK自带... 目录1.问题提出2.如何将Java程序打包成可执行文件2.1将编译后的代码及jre运行环境一起打包2

Java使用Tesseract-OCR实战教程

《Java使用Tesseract-OCR实战教程》本文介绍了如何在Java中使用Tesseract-OCR进行文本提取,包括Tesseract-OCR的安装、中文训练库的配置、依赖库的引入以及具体的代... 目录Java使用Tesseract-OCRTesseract-OCR安装配置中文训练库引入依赖代码实

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行