堆栈跟踪从何而来?

我认为,阅读和理解堆栈跟踪是每个程序员都必须具备的一项基本技能,以便有效地解决每种JVM语言的问题(另请参阅: 过滤日志中无关的堆栈跟踪行和首先记录引起异常的根本原因 )。 那么我们可以从一个小测验开始吗? 给定以下代码,堆栈跟踪中将出现哪些方法? foo()bar()还是两者皆有?
public class Main {public static void main(String[] args) throws IOException {try {foo();} catch (RuntimeException e) {bar(e);}}private static void foo() {throw new RuntimeException('Foo!');}private static void bar(RuntimeException e) {throw e;}
}

在C#中,根据在bar() 重新抛出原始异常的方式,两种答案都是可能的– throw e用再次抛出该异常的位置 bar()bar()覆盖原始堆栈跟踪(起源于foo() bar() )。 。 另一方面,裸' throw '关键字会重新引发异常,从而保持原始堆栈跟踪。 Java遵循第二种方法(使用第一种方法的语法),甚至不允许直接使用前一种方法。 但是这个经过稍微修改的版本呢:

public static void main(String[] args) throws IOException {final RuntimeException e = foo();bar(e);
}private static RuntimeException foo() {return new RuntimeException();
}private static void bar(RuntimeException e) {throw e;
}

foo()仅创建异常,而不是引发异常,而是返回该异常对象。 然后从完全不同的方法引发此异常。 堆栈跟踪现在将如何显示? 令人惊讶的是,它仍然指向foo() ,就像从那里抛出异常一样,与第一个示例完全相同:

Exception in thread 'main' java.lang.RuntimeExceptionat Main.foo(Main.java:7)at Main.main(Main.java:15)

您可能会问发生了什么事? 看起来当抛出异常时不是生成堆栈跟踪,而是在创建异常对象时生成 。 在绝大多数情况下,这些动作都发生在同一位置,因此没有人打扰。 许多新手Java程序员甚至都不知道可以创建一个异常对象并将其分配给变量或字段,甚至可以将其传递出去。

但是,异常堆栈跟踪的真正来源是什么? 答案很简单,来自Throwable.fillInStackTrace()方法!

public class Throwable implements Serializable {public synchronized native Throwable fillInStackTrace();//...
}

请注意,此方法不是final方法,这使我们可以进行一点修改。 我们不仅可以绕过堆栈跟踪的创建并在没有任何上下文的情况下引发异常,甚至可以完全覆盖堆栈!

public class SponsoredException extends RuntimeException {@Overridepublic synchronized Throwable fillInStackTrace() {setStackTrace(new StackTraceElement[]{new StackTraceElement('ADVERTISEMENT', '   If you don't   ', null, 0),new StackTraceElement('ADVERTISEMENT', ' want to see this ', null, 0),new StackTraceElement('ADVERTISEMENT', '     exception    ', null, 0),new StackTraceElement('ADVERTISEMENT', '    please  buy   ', null, 0),new StackTraceElement('ADVERTISEMENT', '   full  version  ', null, 0),new StackTraceElement('ADVERTISEMENT', '  of  the program ', null, 0)});return this;}
}public class ExceptionFromHell extends RuntimeException {public ExceptionFromHell() {super('Catch me if you can');}@Overridepublic synchronized Throwable fillInStackTrace() {return this;}
}

抛出上述异常将导致JVM打印以下错误(认真尝试!)

Exception in thread 'main' SponsoredExceptionat ADVERTISEMENT.   If you don't   (Unknown Source)at ADVERTISEMENT. want to see this (Unknown Source)at ADVERTISEMENT.     exception    (Unknown Source)at ADVERTISEMENT.    please  buy   (Unknown Source)at ADVERTISEMENT.   full  version  (Unknown Source)at ADVERTISEMENT.  of  the program (Unknown Source)Exception in thread 'main' ExceptionFromHell: Catch me if you can

那就对了。 ExceptionFromHell更加有趣。 由于它不包括堆栈跟踪作为异常对象的一部分,因此仅类名和消息可用。 堆栈跟踪丢失了,JVM和任何日志记录框架都无法对此做任何事情。 你到底为什么要这么做(我不是在谈论SponsoredException )?
某些人(?)意外地认为生成堆栈跟踪很昂贵,这是一种native方法,它必须遍历整个堆栈才能构建StackTraceElement 。 我一生中曾经看到过使用这种技术的库来更快地引发异常。 因此,我编写了一个快速的游标卡程序基准测试,以查看抛出正常的RuntimeException和未填充堆栈跟踪的异常与普通方法的返回值之间的性能差异。 我使用递归运行具有不同堆栈跟踪深度的测试:

public class StackTraceBenchmark extends SimpleBenchmark {@Param({'1', '10', '100', '1000'})public int threadDepth;public void timeWithoutException(int reps) throws InterruptedException {while(--reps >= 0) {notThrowing(threadDepth);}}private int notThrowing(int depth) {if(depth <= 0)return depth;return notThrowing(depth - 1);}//--------------------------------------public void timeWithStackTrace(int reps) throws InterruptedException {while(--reps >= 0) {try {throwingWithStackTrace(threadDepth);} catch (RuntimeException e) {}}}private void throwingWithStackTrace(int depth) {if(depth <= 0)throw new RuntimeException();throwingWithStackTrace(depth - 1);}//--------------------------------------public void timeWithoutStackTrace(int reps) throws InterruptedException {while(--reps >= 0) {try {throwingWithoutStackTrace(threadDepth);} catch (RuntimeException e) {}}}private void throwingWithoutStackTrace(int depth) {if(depth <= 0)throw new ExceptionFromHell();throwingWithoutStackTrace(depth - 1);}//--------------------------------------public static void main(String[] args) {Runner.main(StackTraceBenchmark.class, new String[]{'--trials', '1'});}}

结果如下:

我们可以清楚地看到,堆栈跟踪越长,抛出异常所花费的时间就越长。 我们还看到,对于合理的堆栈跟踪长度,抛出异常的时间不应超过100?s(比读取1 MiB主内存快)。 最终,在没有堆栈跟踪的情况下抛出异常的速度提高了2-5倍。 但老实说,如果这对您来说是个问题,那么问题就出在别的地方。 如果您的应用程序经常抛出异常而实际上必须对其进行优化,则您的设计可能存在问题。 然后不要修复Java,它不会损坏。

摘要:

  • 堆栈跟踪始终显示创建异常(对象)的位置,而不是引发异常的位置-尽管在99%的情况下,该位置相同。
  • 您可以完全控制由异常返回的堆栈跟踪
  • 生成堆栈跟踪会带来一些成本,但是如果它成为应用程序的瓶颈,则可能是您做错了什么。

参考: 堆栈跟踪来自何处? 来自我们的JCG合作伙伴 Tomasz Nurkiewicz,来自Java和邻里博客。


翻译自: https://www.javacodegeeks.com/2012/10/where-do-stack-traces-come-from.html

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

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

相关文章

@select 怎么写存储过程_MySQL4:存储过程和函数

什么是存储过程简单说&#xff0c;存储过程就是一条或多条SQL语句的集合&#xff0c;可视为批文件&#xff0c;但是起作用不仅限于批处理。本文主要讲解如何创建存储过程和存储函数以及变量的使用&#xff0c;如何调用、查看、修改、删除存储过程和存储函数等。使用的数据库和表…

跨域访问-预请求及跨域常见问题

预请求 参考&#xff1a;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#预请求 简而言之&#xff0c;在跨域并且尝试添加一些特殊头及自定义头的情况下&#xff0c;由于浏览器的安全机制&#xff0c;会加多一次OPTIONS预请求&#xff08;询问请求&am…

mysql查询优化之一:mysql查询优化常用方式

一、为什么查询速度会慢&#xff1f; 一个查询的生命周期大致可以按照顺序来看&#xff1a;从客户端&#xff0c;到服务器&#xff0c;然后在服务器上进行解析&#xff0c;生成执行计划&#xff0c;执行&#xff0c;并返回结果给客户端。其中在“执行”阶段包含了大量为了检索…

抖音右上角一个小黄点是什么_抖音官方入驻视频号,释放了一个什么样的信号?...

专注视频号观察第 328 篇这几天&#xff0c;视频号生态新入驻了一个企业号&#xff0c;在圈里引起不少的轰动&#xff0c;因为这个号的名字叫做———抖音。这件事在圈里引发不少的轰动&#xff0c;很多人惊叹&#xff1a;“连抖音都来开视频号了&#xff0c;你还在等什么&…

Jenkins:部署JEE工件

随着持续集成和持续交付的出现 &#xff0c;我们的构建被分为不同的步骤&#xff0c;以创建部署管道。 这些步骤中的一些步骤可以是例如编译和运行快速测试&#xff0c;运行慢速测试&#xff0c;运行自动验收测试或发布应用程序等。 部署流程的最后一步意味着将我们的产品&…

seafile 部署_Seafile开启webdav及读写性能测试

为什么要在seafile搞webdavSeafile 一直是一款可靠的文件同步web应用&#xff0c;经过个人测试&#xff0c;同一台机器上&#xff0c;seafile在传输文件时的速度比nextcloud要快&#xff08;可能也与php的设置有关系&#xff09;&#xff0c;这是seafile的优势。但是&#xff0…

Python--校园网爬虫记

查成绩&#xff0c;算分数&#xff0c;每年的综合测评都是个固定的过程&#xff0c;作为软件开发者&#xff0c;这些过程当然可以交给代码去做&#xff0c;通过脚本进行网络请求获取数据&#xff0c;然后直接进行计算得到基础分直接填表就好了&#xff0c;查成绩再手动计算既容…

Spring–添加SpringMVC –第1部分

欢迎来到本教程的第四部分。 在这一部分中&#xff0c;我们将使用Spring MVC编写控制器和视图&#xff0c;并考虑我们的REST模型。 我们必须做的第一件事&#xff0c;就是根据目前的情况制作一个Web应用程序。 我们将web / WEB-INF文件夹添加到我们的项目根目录。 在WEB-INF内创…

access month函数用法_学会了这7个EXCEL日期函数技巧,老板再让你加班,你找我!...

日期函数&#xff0c;常用年月日&#xff0c;时分秒&#xff0c;星期&#xff0c;季度&#xff0c;求差值等&#xff0c;学会以下几个函数&#xff0c;老板再让你加班&#xff0c;你找我&#xff01;1、记录当前时间(不随系统时间变化)NOW()函数与数据有效性结合&#xff0c;记…

meta 的作用 搜集

Meta标签中的format-detection属性及含义 format-detection翻译成中文的意思是“格式检测”&#xff0c;顾名思义&#xff0c;它是用来检测html里的一些格式的&#xff0c;那关于meta的format-detection属性主要是有以下几个设置&#xff1a;<meta name"format-detecti…

ThinkPHP 3.2.x 集成极光推送指北

3.2版本已经过了维护生命周期&#xff0c;官方已经不再维护&#xff0c;请及时更新至5.0版本 —— ThinkPHP 官方仓库 以上&#xff0c;如果有条件&#xff0c;请关闭这个页面&#xff0c;然后升级至 ThinkPHP 5&#xff0c;如果由于各种各样的原因无法升级至 TP 5 &#xff0c…

Java多线程——不变性与安全发布

1、不变性 某个对象在被创建后其状态就不能被修改&#xff0c;那么这个对象就称为不可变对象&#xff0c;不可变对象一定是线程安全的。不可变对象很简单。他们只有一种状态&#xff0c;并且该状态由构造函数来控制。 当满足以下条件时&#xff0c;对象才是不可变的&#xff1a…

中tr不能显示字符_垃圾文本识别中基本操作指南和错误总结,第三部分

创建模型需要用到机器学习的库&#xff0c;所以我们先下载sklearn库sklearn库下载完成后再输入库文件&#xff0c;就可以完美运行。然后就是划分测试集和训练集&#xff0c;需要注意的是&#xff0c;在从数据处理函数中导入数据时&#xff0c;足足运行了有将近30多秒&#xff0…

(转载)20分钟读懂程序集

转自&#xff1a;http://www.cnblogs.com/damonlan/p/3221347.html 说到程序集&#xff0c;我刚开始对这个名词特别的郁闷&#xff01;~。然后 前些天花了些时间 好好读了一下&#xff0c;现在比较清晰了&#xff0c;把一些书上看到的 记下来&#xff0c;以飨读者。希望没浪费你…

大数据胸_喂母乳会导致胸下垂?!你被这个谣言骗了多少年?

很多人认为&#xff0c;给宝宝喂奶会导致胸下垂。有些爱美的妈妈&#xff0c;甚至在宝宝出生6个月后就着急断奶。那么&#xff0c;喂奶真的会导致胸下垂么&#xff1f;给大家讲两个真实的调查结果哈~2004年的一次针对496名新妈妈的调查结果显示&#xff0c;有75%的母乳喂养母亲…

自制ACL+DHCP实验(初版)

&#xff08;实验用gns模拟器&#xff09; ACL 实验拓扑&#xff1a; 实验要求&#xff1a; 1.1.1.1→3.3.3.3 不通 11.11.11.11→3.3.3.3 通 2.2.2.2→3.3.3.3 通 实验步骤&#xff1a; 步骤一&#xff1a;基本配置 R1&#xff1a; R1#conf t R1(config)#int lo0 R1(config-if…

pil 图像最大值_第97天:图像库 PIL(二)

上节我们讲了 Python 的图像处理库 PIL 的基本图像处理功能&#xff0c;打开了 PIL 的神秘面纱。这节我们接着讲 PIL 的 Image 模块的常用方法。Image 模块的方法convertImage.convert(modeNone, matrixNone, ditherNone, palette0, colors256)参数说明&#xff1a;mode&#x…

c#的委托用法delegate

转载于:https://www.cnblogs.com/douzujun/p/6555886.html

np读取csv文件_被 Pandas read_csv 坑了

-- 不怕前路坎坷&#xff0c;只怕从一开始就走错了方向Pandas 是python的一个数据分析包&#xff0c;纳入了大量库和一些标准的数据模型&#xff0c;提供了高效地操作大型数据集所需的工具。Pandas 就是为解决数据分析任务生的&#xff0c;无论是数据分析还是机器学习项目数据预…

铃木uy125摩托车机油_UY125 新瑞梦UM125发布 济南铃木于湖南株洲吹响国IV集结号...

​4月18日&#xff0c;济南铃木在湖南株洲天台开元酒店举行了2019年新品发布会&#xff0c;并于现场发布了两款极具终端战略意义的新款国IV车型&#xff0c;分别为定位“实用运动”的全新个性化踏板车型UY125&#xff0c;以及能够进一步巩固济南铃木在国IV入门级踏板车型领域绝…