纳尼???我JVM优化过头了,直接把异常信息优化没了?怎么办

你好呀,我是why。

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

image

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

先给你上个代码:

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

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

image

没关系,你要没看清楚,我还能给你截个图:

image

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

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

image

你说为啥?

为啥?

这事就得从 2004 年讲起了。

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

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

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

image

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

我们一句句的来:

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

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

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

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

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

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

先说个其他的。

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

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

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

image

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

image

image

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

到底为啥?

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

那么性能问题到底在哪?

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

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

image

最终会走到这个 native 方法:

java.lang.Throwable#fillInStackTrace(int)

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

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

为啥比较耗时呢?

给你看个比较直观的:

image

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

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

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

image

我这里贴一下结论:

image

关于消除异常的性能消耗,他提出了三个解决方案:

重构你的代码不使用它们。
缓存异常实例。
重写 fillInStackTrace 方法。

通过小日…小日子过的还不错的日本的站点,输入关键信息后,知乎的这个链接排在第二个:

image

https://www.zhihu.com/question/21405047

image

这个问题下面,有一个R大的回答,粘贴给你看看:

image

大家都不约而同的提到了重写 fillInStackTrace 方法,这个性能优化小技巧,也就是我们可以这样去自定义异常:

image

用一个不严谨的方式测试一下,你就看这个意思就行:

image

重写了 fillInStackTrace 方法,直接返回 this 的对象,比调用了爬栈方法的原始方法,快了不是一星半点儿。

其实除了重写 fillInStackTrace 方法之外,JDK 7 之后还提供了这样的一个方法:

java.lang.Throwable#Throwable(java.lang.String, java.lang.Throwable, boolean, boolean)

image

可以通过 writableStackTrace 入参来控制是否需要去爬栈。

那么到底什么时候才应该去用这样的一个性能优化手段呢?

其实R大的回答里面说的很清楚了:

image

其实我们写业务代码的,异常信息打印还是非常有必要的。

但是对于一些追求性能的框架,就可以利用这个优势。

比如我在 disruptor 和 kafka 的源码里面都找到了这样的优化落地源码。

先看 disruptor 的:

com.lmax.disruptor.AlertException

image

  • Overridden so the stack trace is not filled in for this exception for performance reasons.
  • 由于性能的原因,重载后的堆栈跟踪不会被填入这个异常。

再看 kafka 的:

org.apache.kafka.common.errors.ApiException

image

  • avoid the expensive and useless stack trace for api exceptions
  • 避免对api异常进行昂贵而无用的堆栈跟踪

而且你注意到了吗,上面着两个框架中,直接把 synchronized 都干掉了。如果你也打算重写,那么也可以分析一下你的场景中是否可以去掉 synchronized,性能又可以来一点提升。

另外,R大的回答里面还提到了这个优化是 C2 的优化。

我们可以简单的证明一下。

分层编译

前面提到的 C2,其实还有一个对应的 C1。这里说的 C1、C2 都是即时编译器。

你要是不熟悉 C1、C2,那我换个说法。

C1 其实就是 Client Compiler,即客户端编译器,特点是编译时间较短但输出代码优化程度较低。

C2 其实就是 Server Compiler,即服务端编译器,特点是编译耗时长但输出代码优化质量也更高。

大家常常提到的 JVM 帮我们做的很多“激进”的为了提升性能的优化,比如内联、快慢速路径分析、窥孔优化,包括本文说的“不显示异常堆栈”,都是 C2 搞的事情。

多说一句,在 JDK 10 的时候呢,又推出了 Graal 编译器,其目的是为了替代 C2。

至于为什么要替换 C2,额,原因之一是这样的…

http://icyfenix.cn/tricks/2020/graalvm/graal-compiler.html

C2 的历史已经非常长了,可以追溯到 Cliff Click 大神读博士期间的作品,这个由 C++ 写成的编译器尽管目前依然效果拔群,但已经复杂到连 Cliff Click 本人都不愿意继续维护的程度。

你看前面我说的 C1、C1 的特点,刚好是互补的。

所以为了在程序启动、响应速度和程序运行效率之间找到一个平衡点,在 JDK 6 之后,JVM 又支持了一种叫做分层编译的模式。

也是为什么大家会说:“Java 代码运行起来会越来越快、Java 代码需要预热”的根本原因和理论支撑。

在这里,我引用《深入理解Java虚拟机HotSpot》一书中 7.2.1 小节[分层编译]的内容,让大家简单了解一下这是个啥玩意。

首先,我们可以使用 -XX:+TieredCompilation 开启分层编译,它额外引入了四个编译层级。

  • 第 0 级:解释执行。
  • 第 1 级:C1 编译,开启所有优化(不带 Profiling)。Profiling 即剖析。
  • 第 2 级:C1 编译,带调用计数和回边计数的 Profiling 信息(受限 Profiling).
  • 第 3 级:C1 编译,带所有Profiling信息(完全Profiling).
  • 第 4 级:C2 编译。

常见的分层编译层级转换路径如下图所示:

image

  • 0→3→4:常见层级转换。用 C1 完全编译,如果后续方法执行足够频繁再转入 4 级。
  • 0→2→3→4:C2 编译器繁忙。先以 2 级快速编译,等收集到足够的 Profiling 信息后再转为3级,最终当 C2 不再繁忙时再转到 4 级。
  • 0→3→1/0→2→1:2/3级编译后因为方法不太重要转为 1 级。如果 C2 无法编译也会转到 1 级。
  • 0→(3→2)→4:C1 编译器繁忙,编译任务既可以等待 C1 也可以快速转到 2 级,然后由 2 级转向 4 级。

如果你之前不知道分层编译这回事,没关系,现在有这样的一个概念就行了。面试不会考的,放心。

接下来,就要提到一个参数了:

-XX:TieredStopAtLevel=___

image

看名字你也知道了,这个参数的作用是让分层编译停在某一层,默认值为 4,也就是到 C2 编译。

那我把该值修改为 3,岂不是就只能用 C1 了,那就不能利用 C2 帮我优化异常啦?

实验一波:

image

果然如此,R大诚不欺我。

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299467.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 读写ACCESS的OLE对象,演示图片与长文件的读写

网络上的读写OLE对象的代码是多&#xff0c;不过多是转载的&#xff0c;大部分人从来都没实际测试过&#xff0c;只是COPY来COPY去。我重来没看到一个真正可以运行的东东。没办法&#xff0c;只有自力更生&#xff0c;花了一点时间出了点研究成果&#xff0c;写到这里做个记录。…

WPF等待动画

WPF开发者QQ群&#xff1a; 340500857 | 微信群 -> 进入公众号主页 加入组织欢迎转发、分享、点赞、在看&#xff0c;谢谢~。 01—效果预览效果预览&#xff08;更多效果请下载源码体验&#xff09;&#xff1a;02—代码如下一、CycleLoading.cs 代码如下using System.Win…

假期别在家里要发霉了?可以靠他们度过无聊时光

全世界只有3.14 % 的人关注了青少年数学之旅在这个资讯丰富且易获取的时代&#xff0c;越来越多的人不愿意花时间阅读书籍&#xff0c;碎片化阅读成了主流。人们获取的东西多而杂&#xff0c;很难系统、全面。海量信息对人是冲击&#xff0c;更是诱惑。谁不想了解天下奇闻&…

cs6序列号 mac版photoshop_重磅!Parallels Desktop 16 M1版发布

Parallels 现已发布 Parallels Desktop 16 技术预览版&#xff0c;可以在搭载 M1 芯片的 Mac 电脑上运行。该公司表示&#xff0c;它创建了一个 “使用 Apple M1 Mac 芯片硬件辅助虚拟化的新虚拟化引擎”&#xff0c;允许用户在虚拟机中运行基于 Arm 的操作系统&#xff0c;例如…

华为二面!!!面试官直接问我Java中到底什么是NIO?这不是直接送分题???

华为二面&#xff01;&#xff01;&#xff01;面试官直接问我Java中到底什么是NIO&#xff1f;这不是直接送分题&#xff1f;&#xff1f;&#xff1f;什么是NIO缓冲区(Buffer)缓冲区类型获取缓冲区核心属性核心方法非直接缓冲区和直接缓冲区非直接缓冲区直接缓冲区通道(Chann…

Delphi中的容器类(3)

TBucketList和TObjectBucketList类 从Delphi6开始&#xff0c;VCL的Contnrs单元中又增加了两个新的容器类TBucketList和TObjectBucketList。TBucketList实际上也是一个简单基于哈希表的指针-指针对列表。接口定义如下&#xff1a; TBucketList class(TCustomBucketList)… pu…

一文读懂哈希和一致性哈希算法

哈希 Hash 算法介绍哈希算法也叫散列算法, 不过英文单词都是 Hash, 简单一句话概括, 就是可以把任意长度的输入信息通过算法变换成固定长度的输出信息, 输出信息也就是哈希值, 通常哈希值的格式是16进制或者是10进制, 比如下面的使用 md5 哈希算法的示例md5("123456"…

延迟开学?这些教育读书公众号可以帮助孩子学习! 你都关注了吗?

全世界只有3.14 % 的人关注了青少年数学之旅受新型冠状病毒疫情影响&#xff0c;日前&#xff0c;教育部印发《关于2020年春季学期延期开学的通知》。推迟开学时间&#xff0c;意味着寒假的延长。为此&#xff0c;小编精选这些优质的教育号和读书号帮助孩子学习&#xff01;理想…

go方法的深入探究(7.21增补)

2019独角兽企业重金招聘Python工程师标准>>> 1&#xff09;哪些类型可以有方法&#xff1a; 1&#xff09;只能对命名类型和命名类型的指针编写方法&#xff1b; 2&#xff09;不能对接口类型和接口类型的指针编写方法&#xff1b; 3&#xff09;只能在定义命名类型…

element文件上传有文件但是后台接收为空_程序员提高篇:大规格文件(G)是如何做分片优化的?...

作者&#xff1a;凹凸实验室 链接&#xff1a;https://juejin.im/post/5ebb4346e51d451ef53793ad整体思路第一步是结合项目背景&#xff0c;调研比较优化的解决方案。 文件上传失败是老生常谈的问题&#xff0c;常用方案是将一个大文件切片成多个小文件&#xff0c;并行请求接口…

你连简单的枚举类都不知道,还敢说自己会Java???滚出我的公司

枚举类型是Java 5中新增的特性&#xff0c;它是一种特殊的数据类型&#xff0c;之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束&#xff0c;但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。当需要定义一组常量时&#xff0c;强烈建议使…

绝对硬核!万物有“理”,科学原来如此有趣!

▲ 点击查看假如在生活中&#xff0c;你不小心将生鸡蛋和熟鸡蛋混在一起了&#xff0c;那么此时你要如何分辨&#xff0c;哪个鸡蛋是生的&#xff0c;哪个是熟的呢&#xff1f;假若你曾学过力学&#xff0c;那你一定能够轻易的分辨这个生熟问题。我们把这两个鸡蛋放在桌上&…

微软输入法2010下载使用-IME2010下载使用

3年前&#xff0c;写过IME2007的安装和使用&#xff0c;在Office 2010 beta开放之后&#xff0c;觉得单独把ime2010单独开放出来比较适合Office 2003/2007的用户群。 1。 依然还是和上次的IME2007提取方式一样&#xff0c;先用7-zip解压Office 2007 beta的exe文件&#xff1a;由…

理论修炼之RabbitMQ,消息队列服务的稳健者

????欢迎点赞 &#xff1a;???? 收藏 ⭐留言 ???? 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;????本文作者&#xff1a;由webmote 原创&#xff0c;首发于 【掘金】????作者格言&#xff1a;生活在于折腾&#xff0c;当你不折…

为什么年龄大了近视还增加_年龄明明一样大,为什么有人长得年轻,有人显老呢?...

台湾不老男神林志颖&#xff0c;始终是十几年前演偶像剧的脸。而德云社郭德纲与他是同龄人却饱经沧桑显得更加老相。这是一件让人哭笑不得的事&#xff0c;也被很多人编成段子。那么为什么有些人看起来年轻有些人却老的很快呢&#xff1f;哪一种更长寿呢&#xff1f;接下来让我…

利用Asp.net中的AJAX制作网页上自动选取开始日期及结束日期的用户自定义控件...

前段时间用过一个酒店入住预约网站&#xff0c;当你点击"入住时间"时会悬浮出一对并列的日历&#xff0c;然后点击左边的日历就能选择入住时间&#xff0c;点击右侧的日历就能自动得到离店时间&#xff0c;当时没有太留意是怎么实现的&#xff0c;现在在做项目时&…

【00】架构型

为什么80%的码农都做不了架构师&#xff1f;>>> 1、架构型&#xff08;archetype&#xff09; 一种形式&#xff0c;所有的东西或多或少地遵守。一种形式&#xff0c;属于同一类型的类都或多或少地遵守&#xff0c;包括属性、链接、方法、插入点、交互。 2、领域无…

SQL进阶提升(疑惑篇order by)-学习sql server2005 step by step(十一)

这篇主要发出两个疑惑&#xff0c;希望有兴趣的人解答&#xff0c;谢谢&#xff01; 1.newid()疑惑 1 create table tb (aa int,bb char(1)) 2 insert tb values(1,A) 3 insert tb values(1,B) 4 insert tb values(1,C) 5 insert tb values(1,D) 6 7 insert tb value…

钟南山团队在患者粪便中检出新冠活病毒,国家卫健委回应!专家:做好这事很重要...

全世界只有3.14 % 的人关注了青少年数学之旅2月13日下午&#xff0c;在广东省人民政府新闻办召开的疫情防控新闻发布会上&#xff0c;钟南山院士团队成员、广州医科大学国家呼吸疾病重点实验室副主任、教授赵金存介绍&#xff0c;该团队在P3实验室中&#xff0c;在中山大学附属…

CSDN《某一程序员竟然吃过shi?让我们走进他的生活,揭露背后的故事》

CSDN《某一程序员竟然吃过屎&#xff1f;我们走进他的生活&#xff0c;揭露背后的故事》 ——————————接下来让我们走进他的故事 到底是什么原因让他吃屎 这是这位程序员的自曝&#xff0c;我很好奇的不是他吃过屎&#xff0c;我在好奇是啥味的~ 接下来我们开始咨询这…