堆栈跟踪从何而来?

我认为,阅读和理解堆栈跟踪是每个程序员都必须具备的一项基本技能,以便有效地解决每种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;并返回结果给客户端。其中在“执行”阶段包含了大量为了检索…

修复Sonar中常见的Java安全代码冲突

本文旨在向您展示如何快速修复最常见的Java安全代码冲突。 它假定您熟悉代码规则和违规的概念以及Sonar如何对其进行报告。 但是&#xff0c;如果您以前从未听说过这些术语&#xff0c;则可以阅读Sonar Concepts或即将出版的有关Sonar的书 &#xff0c;以获取更详细的解释。 为…

理解ThreadLocal

ThreadLocal:线程本地存储&#xff0c;为每个线程都创建了变量的副本&#xff0c;线程在访问变量时&#xff0c;可以直接访问自己内部的副本变量。 理解几个概念&#xff1a; 在java中ThreadLocal是一个类。 ThreadMap是一个类&#xff0c; Thread类是线程类。 ThreadLocal…

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

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

资源包技巧和最佳实践

今天是资源捆绑日。 通常&#xff0c;这是Java中最著名的国际化机制&#xff08;i18n&#xff09;。 使用它应该很容易。 但是&#xff0c;在弄脏手时会出现许多小问题。 如果您有相同的想法&#xff0c;则此文章适合您。 基本 java.util.ResourceBundle定义了用于访问Java中翻…

springMvc-文件上传

赶时间&#xff0c;又在写垃圾博客&#xff0c;在心里给自己一耳巴 1.单文件上传 2.多文件上传 代码&#xff1a; 页面&#xff1a; <!DOCTYPE html><html><head><meta charset"UTF-8"><title>Insert title here</title></he…

c cuda 指定gpu_GPU并行编程:熟练使用CUDA C语言

【IT168 专稿】一个大任务通常可能被分解成许多可以一起处理的小任务&#xff0c;以便创建一个解决方案&#xff0c;这和粉刷房子的道理是一样的&#xff0c;在粉刷之前&#xff0c;假设你需要买5公升油漆和5把刷子&#xff0c;你可以自己一个人干完采购和粉刷的活&#xff0c;…

js中使用0 “” null undefined {}需要注意

注意&#xff1a;在js中0为空&#xff08;false&#xff09; &#xff0c;代表空的还有“”&#xff0c;null &#xff0c;undefined&#xff1b; 如果做判断if(&#xff01;上面的四种值)&#xff1b;返回均为false console.log(!null);// true console.log(!0);//true consol…

PhpStorm 10.0.3破解版下载

汉化破解版软件下载&#xff1a; http://pan.baidu.com/s/1geNO24r 密码: d5ci 这个汉化破解软件解决了大纲视图里空白的问题。 先安装腾讯电脑管家&#xff0c;然后安装这个软件&#xff0c;安装到最后提示有个文件有病毒已删除&#xff0c;点确定后正常使用。转载于:https://…

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内创…

[Linux] 权限与指令间的关系

我们知道权限对于使用者帐号来说是非常重要的&#xff0c;因为他可以限制使用者能不能读取/创建/删除/修改文件或目录&#xff01; 在这一章我们介绍了很多文件系统的管理指令&#xff0c;第五章则介绍了很多文件权限的意义。在这个小节当中&#xff0c; 我们就将这两者结合起来…

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

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

css样式表的选择器与分类

css 样式表的作用&#xff1a; 主要用于结构,样式与行为,CSS主要的作用就是美化网页的一个语言,它的特点: 1.结构与样式分离的方式,便于后期维护与改版; 2.样式定义精确到像素的级别; css样式表的结构&#xff1a;CSS 称为层叠样式表 用于给网页设置各种样式 css样式的语法由3部…

Spring 3.1缓存和@Cacheable

缓存在软件领域已经存在很长时间了。 它们是那些真正有用的东西之一&#xff0c;一旦您开始使用它们&#xff0c;您会想知道如果没有它们&#xff0c;您是如何相处的&#xff0c;所以似乎让Spring的家伙们只是在版本中向Spring核心添加缓存实现有点奇怪。 3.1。 我猜想以前没有…

pytorchyolov4训练_使用pytorch-yolov5 訓練自己的數據集-2020.6.15

make yolov5 pytorch train datasets训练所需环境 python3.5, pytorch1.3, torchvision 0.4.1 , tensorboard 1.14.0 , tensorflow-gpu1.14.0本例制作yolov5数据集 并进行数据训练从VOC数据集转为训练所需的coco数据集代码有待改进包含文件夹voc2coco/(Annotations/ JPEGImages…