高级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

相关文章

Pyserial设置缓冲区大小失败的问题解决

《Pyserial设置缓冲区大小失败的问题解决》本文主要介绍了Pyserial设置缓冲区大小失败的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录问题描述原因分析解决方案问题描述使用set_buffer_size()设置缓冲区大小后,buf

PyInstaller打包selenium-wire过程中常见问题和解决指南

《PyInstaller打包selenium-wire过程中常见问题和解决指南》常用的打包工具PyInstaller能将Python项目打包成单个可执行文件,但也会因为兼容性问题和路径管理而出现各种运... 目录前言1. 背景2. 可能遇到的问题概述3. PyInstaller 打包步骤及参数配置4. 依赖

Spring Boot项目部署命令java -jar的各种参数及作用详解

《SpringBoot项目部署命令java-jar的各种参数及作用详解》:本文主要介绍SpringBoot项目部署命令java-jar的各种参数及作用的相关资料,包括设置内存大小、垃圾回收... 目录前言一、基础命令结构二、常见的 Java 命令参数1. 设置内存大小2. 配置垃圾回收器3. 配置线程栈大小

SpringBoot实现微信小程序支付功能

《SpringBoot实现微信小程序支付功能》小程序支付功能已成为众多应用的核心需求之一,本文主要介绍了SpringBoot实现微信小程序支付功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作... 目录一、引言二、准备工作(一)微信支付商户平台配置(二)Spring Boot项目搭建(三)配置文件

解决SpringBoot启动报错:Failed to load property source from location 'classpath:/application.yml'

《解决SpringBoot启动报错:Failedtoloadpropertysourcefromlocationclasspath:/application.yml问题》这篇文章主要介绍... 目录在启动SpringBoot项目时报如下错误原因可能是1.yml中语法错误2.yml文件格式是GBK总结在启动S

Spring中配置ContextLoaderListener方式

《Spring中配置ContextLoaderListener方式》:本文主要介绍Spring中配置ContextLoaderListener方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录Spring中配置ContextLoaderLishttp://www.chinasem.cntene

java实现延迟/超时/定时问题

《java实现延迟/超时/定时问题》:本文主要介绍java实现延迟/超时/定时问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java实现延迟/超时/定时java 每间隔5秒执行一次,一共执行5次然后结束scheduleAtFixedRate 和 schedu

Java Optional避免空指针异常的实现

《JavaOptional避免空指针异常的实现》空指针异常一直是困扰开发者的常见问题之一,本文主要介绍了JavaOptional避免空指针异常的实现,帮助开发者编写更健壮、可读性更高的代码,减少因... 目录一、Optional 概述二、Optional 的创建三、Optional 的常用方法四、Optio

Spring Boot项目中结合MyBatis实现MySQL的自动主从切换功能

《SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能》:本文主要介绍SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能,本文分步骤给大家介绍的... 目录原理解析1. mysql主从复制(Master-Slave Replication)2. 读写分离3.

idea maven编译报错Java heap space的解决方法

《ideamaven编译报错Javaheapspace的解决方法》这篇文章主要为大家详细介绍了ideamaven编译报错Javaheapspace的相关解决方法,文中的示例代码讲解详细,感兴趣的... 目录1.增加 Maven 编译的堆内存2. 增加 IntelliJ IDEA 的堆内存3. 优化 Mave