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

刚开始我的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);
}

终于,世界都清净了~