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

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置