高级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余个。紧急处理数据库生产事故上百起,挽回数据丢失所操作的灾难损失不计其数。

在这里插入图片描述

在这里插入图片描述

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术很渣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值