JVM优化过头了,直接把异常信息优化没了? (上)

简介: JVM优化过头了,直接把异常信息优化没了? (上)

你好呀,我是why。

你猜这次我又要写个啥没有卵用的知识点呢?

image.png

不好意思,问的稍微有点早了,啥提示都没给,咋猜呢,对吧?

先给你上个代码:

public class ExceptionTest {
    public static void main(String[] args) {
        String msg = null;
        for (int i = 0; i < 500000; i++) {
            try {
                msg.toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

来,就这代码,你猜猜写出个什么花儿来?

当然了,有猜到的朋友,也有没猜到的朋友。

很好,那么请猜出来了的同学迅速拉到文末,完成一键三连的任务后,就可以出去了。

没有猜出来的同学,我把代码一跑起来,你就知道我要说啥了:

image.png

一瞬间的事儿,瞅见了吗?神奇吗?产生疑问了吗?

image.png

image.png

在抛出一定次数的空指针异常后,异常堆栈没了。

这就是我标题说的:太扯了吧?异常信息突然就没了。

image.png


你说为啥?


为啥?


这事就得从 2004 年讲起了。

那一年,SUN 公司于 9 月 30 日 18 点发布了 JDK 5。

在其 release-notes 中有这样一段话:

https://www.oracle.com/java/technologies/javase/release-notes-introduction.html


image.png


主要是框起来的这句话,看不明白没关系,我用我八级半的英语给你翻译一下。

我们一句句的来:

The compiler in the server VM now provides correct stack backtraces for all "cold" built-in exceptions.

对于所有的内置异常,编译器都可以提供正确的异常堆栈的回溯。

For performance purposes, when such an exception is thrown a few times, the method may be recompiled.

出于性能的考虑,当一个异常被抛出若干次后,该方法可能会被重新编译。(重要)

After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace.

在重新编译之后,编译器可能会选择一种更快的策略,即不提供异常堆栈跟踪的预分配异常。(重要)

To disable completely the use of preallocated exceptions, use this new flag: -XX:-OmitStackTraceInFastThrow.

如果要禁止使用预分配的异常,请使用这个新参数:-XX:-OmitStackTraceInFastThrow。

这几句话先不管理解没有。但是至少知道它这里描述的场景不就是刚刚代码演示的场景吗?

它最后提到了一个参数 -XX:-OmitStackTraceInFastThrow,二话不说,先拿来用了,看看效果再说:


image.png

同样的代码,加入该启动参数后,异常堆栈确实会从头到尾一直打印。

不知道你感觉到没有,加入该启动参数后,程序运行时间明显慢了很多。

在我的机器上没加该参数,程序运行时间是 2826 ms,加上该参数运行时间是 5885 ms。

说明确实是有提升性能的功能。

到底是咋提升的,下一节说。

先说个其他的。

这里都提到 JVM 参数了,我顺便再分享一个网站:

https://club.perfma.com/topic/OmitStackTraceInFastThrow

该网站提供了很多功能,这是其中的几个功能:

image.png

JVM 参数查询功能那必须得有:

image.png

很好用的,你以后遇到不知道是干啥用的 JVM 参数,可以在这个网站上查询一下。


到底为啥?


前面讲了是出于性能原因,从 JDK 5 开始会出现异常堆栈丢失的现象。

那么性能问题到底在哪?

来,我们一起看一下最常见的空指针异常。

以本文为例,看一下异常抛出的时候调用路径:

image.png

最终会走到这个 native 方法:

java.lang.Throwable#fillInStackTrace(int)

fill In Stack Trace,顾名思义,填入堆栈跟踪。

这个方法会去爬堆栈,而这个过程就是一个相对比较消耗性能的过程。

为啥比较耗时呢?

给你看个比较直观的:


image.png

这类的异常堆栈才是我们比较常见的,这么长的堆栈信息,可不消耗性能吗。

现在,我们现在再回去看这句话:

For performance purposes, when such an exception is thrown a few times, the method may be recompiled. After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace.

出于性能的考虑,当一个异常被抛出若干次后,该方法可能会被重新编译。在重新编译之后,编译器可能会选择一种更快的策略,即不提供异常堆栈跟踪的预分配异常。

所以,你能明白,这个“出于性能的考虑”这句话,具体指的就是节约 fillInStackTrace(爬堆栈)的这个性能消耗。

更加深入一点的研究对比,你可以看看这个链接:

http://java-performance.info/throwing-an-exception-in-java-is-very-slow

目录
相关文章
|
4月前
|
缓存 安全 算法
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
47 0
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
45 3
|
1月前
|
存储 算法 Java
深入理解Java虚拟机(JVM)及其优化策略
【10月更文挑战第10天】深入理解Java虚拟机(JVM)及其优化策略
41 1
|
1月前
|
监控 Java
Java的JVM如何优化?
Java的JVM如何优化?
57 3
|
4月前
|
开发者 Java
JVM内存问题之top命令的物理内存信息中,'used'和'free','avail Mem'分别表示什么
JVM内存问题之top命令的物理内存信息中,'used'和'free','avail Mem'分别表示什么
|
4月前
|
Arthas 存储 监控
JVM内存问题之JNI内存泄漏没有关联的异常类型吗
JVM内存问题之JNI内存泄漏没有关联的异常类型吗
|
4月前
|
缓存 Prometheus 监控
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
94 3
|
4月前
|
缓存 算法 Java
JVM内存溢出(OutOfMemory)异常排查与解决方法
JVM内存溢出(OutOfMemory)异常排查与解决方法
|
4月前
|
监控 Java 中间件
FGC频繁导致CPU 飙升定位及JVM配置优化总结
FGC频繁导致CPU 飙升定位及JVM配置优化总结
162 0
|
4月前
|
存储 设计模式 监控
Java面试题:简述JVM的内存结构,包括堆、栈、方法区等。栈内存优化的方法有 哪些?
Java面试题:简述JVM的内存结构,包括堆、栈、方法区等。栈内存优化的方法有 哪些?
47 0