Java运行时故障排查

最近单独负责一个应用上线,由于经验不足,踩了很多坑,记录一下,方便以后查看。

刚开始我的try,catch是这样写的:

try {
    mediaType = detector.detect(inputStream, metadata);
    parser.parse(inputStream, handler, metadata, parseContext);
} catch (TikaException e) {
    handleTikaExcetion(e);
} catch (SAXException e) {
    throw new BadRequestException("文件不符合SAX标准");
} catch (IOException e) {
    return handleIOException(e);
} catch (RuntimeException e) {
    throw new BadRequestException(e.getMessage());
}

结果发现应用跑着跑着就挂了,而且还没有报警…

一脸蒙逼,其实我不介意你挂,最起码你得给我个报警什么的吧…

跑到服务器上,去查了一下日志,果真什么都没有… 好吧,重现了一遍故障,发现了如下内容:

java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2367) ~[na:1.8.0_45]
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:130) ~[na:1.8.0_45]
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:114) ~[na:1.8.0_45]
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:415) ~[na:1.8.0_45]
at java.lang.StringBuilder.append(StringBuilder.java:132) ~[na:1.8.0_45]

虚拟机崩了,然而我并没有捕获Error,于是它就悄无声息的离开了我…

于是修改了代码,就像这样子(注意不要被我带坏,这个地方对业务来说忽略时合理的):

try {
    mediaType = detector.detect(inputStream, metadata);
    parser.parse(inputStream, handler, metadata, parseContext);
} catch (TikaException e) {
    handleTikaExcetion(e);
} catch (SAXException e) {
    throw new BadRequestException("文件不符合SAX标准");
} catch (IOException e) {
    return handleIOException(e);
} catch (RuntimeException e) {
    throw new BadRequestException(e.getMessage());
} catch (Throwable ignored) {
}

过了不多久,发现虚拟机又没响应了,对,是又。

不过这次很奇怪,虚拟机没挂,只是好像阻塞住了,莫非是死锁?

于是用jstack -pid查看了一下它究竟在做什么,竟然发现它真的在阻塞。

就像这样:

"com.package.FullIndex.main()" #30 prio=5 os_prio=31 tid=0x00007f89ca2e7000 nid=0x6713 runnable in Object.wait() [0x0000000107c68000]
    at com.package.FullIndex.main(FullIndex.java:22)
"main" #1 prio=5 os_prio=31 tid=0x00007f89c2029000 nid=0x1303 in Object.wait() [0x0000000107c68000]
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:47)

追着调用栈,找到了阻塞的原因:被传入了一个没有设置读取超时时间的来自网络的InputStream。

于是换另一个接口,自己用socket拿InputStream,终于,世界清静了。

但是我还是too yong,too simple。不一会程序又因为OOM挂了,只不过这次OOM又换了一个地方…。我这个业务只是过来索引富文本文档,3G内存还不够用?

找到了挂掉的对应记录,看起来是这样的:

path=’/bio/soapsnp/kasalath_genome/all.fasta/’,size=409164635

400M的文件,吞进内存够存7份了,为什么不够用?还有这个后缀是什么鬼…

去Google了一下,这份文件是人类基因组描述人类基因的文件…

人类就能随便欺负程序猿吗?为什么读到内存就OOM?

于是,用jmap和jhat看了一眼虚拟机的内存,居然有一个2.6G的char数组。

原来 ISO-8859-1编码转成unicode编码之后,大了那么多…

最后在循环的地方又加了一遍try-catch,用来规避提取内容之外的OOM,并且加入了截断的功能。

加完后看起来是这样的:

try {
    msgHandleService.singleAction(msgModel, SyncEnum.create);
} catch (BadRequestException ignored) {
} catch (RetryException e) {
    indexStorage(storageTree, retryTime + 1);
    return;
} catch (Throwable error) { // ignore it and retry later
    LOG.error(msgModel.toString(), error);
}

终于,世界都清净了~

时间: 2024-07-31 07:50:22

Java运行时故障排查的相关文章

深入理解java虚拟机一 JAVA运行时内存区域与class文件

一 JAVA运行时内存区域 JVM在加载class文件时,会将class文件定义的数据结构转为运行时内存中的数据,那么jvm是如何安排运行时的内存区域呢? jvm将运行时内存划分为以下几个部分: 堆:所有线程共享 方法区:类信息.静态变量.常量等 运行时常量池:class文件的常量池(字面常量和符号引用)+运行时产生的常量 程序计数器:  当前线程执行的字节码的行号指示器 虚拟机栈:栈帧 = 本地局部变量表.操作数栈.动态链接.出口信息 本地方法栈:native方法 直接内存:不属于jvm管理,

Jvm基础-Java运行时数据区

最近在看<深入理解Java虚拟机>,里面讲到了Java运行时数据区,这是Jvm基本知识,把读书笔记记录在此.这些知识属于常识,都能查到的,如果我有理解不对的地方,还请指出. 首先把图贴上来,图来自JVM Runtime Data Areas(运行时数据区),感谢. 由上图可知,Java运行时数据区域包括程序计数器.Java虚拟机栈.本地方法栈.Java堆.方法区. 1. 程序计数器 程序计数器用来记录下一条字节码指令,因为CPU是要轮转的,在切换回来之后,Java能够找到下一条要执行的指令.如

java运行时内存模式学习

学习java运行时内存模式: 各区介绍: 方法区(线程共享):用于存放被虚拟机加载的类的元数据:静态变量,常量,以及编译和的代码(字节码),也称为永久代(所有该类的实例被回收,或者此类classLoader被回收). Java堆(线程共享):存放对象实例和数组,这里是内存回收的主要地方.可以分为新生代(young)和年老代(tenured).从字面也可以知道,新生代存放刚刚建立的对象 而年老代存放长久没有被垃圾回收机制回收的对象.一般新生代有分为eden,from survivor和to sur

深入理解Java运行时数据区

前情回顾 在本专栏的前12篇博客中, 我们主要大致介绍了什么是JVM, 并且详细介绍了class文件的格式. 对于深入理解Java, 或者深入理解运行于JVM上的其他语言, 深入理解class文件格式都是必须的. 如果读者对class文件的格式不是很熟悉, 在阅读本博客下面的文章之前, 建议先读一下前面的12篇博客, 或者参考其他资料, 熟悉class文件的格式. 在深入理解Java虚拟机到底是什么 这篇博客中, 我们有提到过, JVM就是一个特殊的进程, 我们执行的java程序, 都运行在一个

Java运行时数据区域划分

Java运行时数据区域划分 Java JVM 内存 堆 栈 1. 概述 对于Java程序员来说,在虚拟机自动内存管理机制下,不容易出现内存泄漏和内存溢出现象.但如果不了解虚拟机是如何使用内存的,一旦出现了内存泄漏和溢出方面的问题,那么排错就无从下手了. 2. 运行时数据区域 Java虚拟机在执行Java程序的过程中会将它所管理的内存划分为若干个不同的数据区域,如下图所示. 2.1. 程序计数器 程序计数器(Program Counter Register):是一块较小的内存空间,可以看做是当前线

Java运行时数据区

Java虚拟机定义了一些程序运行期间会使用到的数据区域,其中一些会随着JVM的启动而创建,随着JVM的退出而销毁:另外一些则与线程的运行一一对立的,这些数据区域会随着线程的开始而创建,随着线程的结束而销毁.下面是一张Java运行时的数据区模型图: 总的来说,Java运行时数据区域可以分为两个部分:线程共享的区域和线程独享的区域.下面一一对之进行总结. 一.线程共享区域:线程共享区域是指各个线程都会使用到的一块空间区域,它们会在这里申请空间.使用空间.根据具体提供功能不同,可以划分为两个部分,分别

java运行时多态性的实现

运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成"一个接口,多个方法".Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制,下面就继承和接口实现两方面谈谈java运行时多态性的实现. 一.通过继承中超类对象引用变量引用子类对象来实现 举例说明: //定义超类superA class superA { int i = 100; void fun() { System.out.println("This 

Java运行时环境JPEGImageWriter.writeImage函数整数溢出漏洞_

在使用PDFBOX的接口,代码如下: PDFImageWriter imageWriter = new PDFImageWriter(); imageWriter.writeImage(pdDoc, imageType, null, startPage, endPage, imageFilePath, 1, Constants.NUM_TWO_HUNDRED),发现图片生成了,但是报内存溢出错误.后面看了下源代码搜寻相关资料发现存在这样一个问题,所以更换JDK就OK了. Java运行时环境的JP

Java运行时内存

对于java程序员来说,并不必显示地对内存进行管理,一切都交给java虚拟机去做吧,而且,你也不一定做得比java虚拟机来得专业.好像所有内存管理都交给虚拟机去做就万事大吉了,但是,事实有时并非如此,可能有时你会遇到一些让你困惑的问题,如OutOfMemoryError异常,如stackOverflowError,你开始大呼,虚拟机不是都为我们管理好内存了吗?怎么还会出现这样的Error,其实当你真正去了解java虚拟机内存区域的分布的时候,你就会不自觉的大呼:原来java虚拟机也不是万能. 这