内核堆栈 用户堆栈_堆栈痕迹从何而来?

内核堆栈 用户堆栈

我相信,阅读和理解堆栈跟踪是每个程序员都必须具备的一项基本技能,以便有效地解决每种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/352333.shtml

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

相关文章

Java中使用log4j记录日志

在项目开发中&#xff0c;记录错误日志是一个很有必要功能。一是方便调试&#xff1b;二是便于发现系统运行过程中的错误&#xff1b;三是存储业务数据&#xff0c;便于后期分析&#xff1b; 在java中&#xff0c;记录日志&#xff0c;有很多种方式。 比如&#xff0c;自己实…

python如何输出两列数据_如何用python将一列数据分为两列?

引用来自“Ashkandi”的评论[{date: c[:10], time: c[11:]} for c in Col] 引用来自“vx2008”的评论 谢谢回复&#xff0c;这行代码执行完了以后&#xff0c;日期和时间都村到哪里了呢&#xff1f; 我是了一下&#xff0c;调用date&#xff0c;提示没有定义date&#xff0c;请…

Q 语言初学者系列:(3)Lists 初级

声明&#xff1a;本系列文章全部参考自官方教程&#xff0c;由于缺乏中文资料而且本人E文实在太菜&#xff0c;对于E文较好的朋友可以直接通过下面的链接访问官方网站提供的教程&#xff0c; 欢迎大家一起学习讨论。 hhttps://code.kx.com/trac/wiki/QforMortals2/contents 用户…

数据库图书管理建表与修改表

根据调研选定实体及相应属性画出E-R图 ​ 2、将E-R模型转换为对应关系模型&#xff0c;并指出主码和外码 图书book&#xff08;书号&#xff0c;类别&#xff0c;出版社&#xff0c;作者&#xff0c;书名&#xff0c;定价&#xff0c;备注&#xff09;&#xff1b; 读者reade…

如何指南:Apache Drill入门

Apache Drill是一种引擎&#xff0c;可以连接到许多不同的数据源&#xff0c;并为它们提供SQL接口。 它不仅是想跳入任何复杂事物的SQL接口&#xff0c;而且是一个功能强大的接口&#xff0c; 其中包括对许多内置函数和窗口函数的支持。 尽管它可以连接到您仍然可以使用SQL查询…

python3编程入门_在类Unix系统上开始Python3编程入门

>>> s Hello >>> x len(s) >>> print("The length of %s is %d" % (s,x)) The length of Hello is 5 看看《Python基础编程》中对格式化输出的总结&#xff1a; (1). %字符&#xff1a;标记转换说明符的开始 (2). 转换标志&#xff1a;-…

visual studio 调试时提示 已加载“C:\Windows\SysWOW64\ntdll.dll”。无法查找或打开 PDB 文件。

问题描述 “Win32Project3.exe”(Win32): 已加载“D:\software\VS2013\VS2013 文档\Win32Project3\Debug\Win32Project3.exe”。已加载符号。 “Win32Project3.exe”(Win32): 已加载“C:\Windows\SysWOW64\ntdll.dll”。无法查找或打开 PDB 文件。 “Win32Project3.exe”(Wi…

matlab的一个疑问?

把逻辑值放入一个已知矩阵&#xff0c;为啥结果是&#xff1a;真就取矩阵的值&#xff0c;假就不取值&#xff1f; K>> aaaarandi(10,10,2) aaaa 6 3 10 4 6 7 5 2 6 3 8 2 1 2 9 4 2 9 5 5 K>> aaaa9 ans 102 logical array 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 …

如何使用log4j记录日志

【1】从零开始 a). 新建Java Project>>新建package>>新建java类&#xff1b; b). import jar包&#xff08;一个就够&#xff09;&#xff0c;这里我用的是log4j-1.2.14.jar&#xff0c; c). 新建log4j.properties&#xff0c;置于project根目录下&#xff1b; log…

python验证码 识别代码不准_谈谈Python进行验证码识别的一些想法

用python加“验证码”为关键词在baidu里搜一下&#xff0c;可以找到很多关于验证码识别的文章。我大体看了一下&#xff0c;主要方法有几类&#xff1a;一类是通过对图片进行处理&#xff0c;然后利用字库特征匹配的方法&#xff0c;一类是图片处理后建立字符对应字典&#xff…

matlab 安装jdbc.jar

加到matlab的启动项下面。 方法如下&#xff1a;找到matlab的安装路径&#xff0c;如下&#xff1a; 有一个classpath的txt文件&#xff0c;打开&#xff0c;在末尾添加jdbc的jar包路径即可&#xff0c;如下&#xff1a; 或者&#xff0c;直接在matlab 上用以下命令&#xff0…

MyEclipse软件中快捷键

在调试程序的时候&#xff0c;我们经常需要注释一些代码&#xff0c;在用Myeclipse编程时&#xff0c;就可以用 Ctrl/ 为选中的一段代码加上以 // 打头的注释&#xff1b;当需要恢复代码功能的时候&#xff0c;又可以用Ctrl/ 去掉注释。这样的快捷键确实让我们编程变得容易多了…

python3:(unicode error) 'utf-8' codec can't decode

操作系统&#xff1a;win7 旗舰版 语言&#xff1a;python3.4 文本编辑器&#xff1a;vim、notepad 报错&#xff1a;SyntaxError: (unicode error) utf-8 codec cant decode byte 0xb4 in position 0:invalid start byte 基本知识&#xff1a;在python中默认的编码格式是 utf-…

传统的Web应用程序和RESTful API

如今&#xff0c;当我们构建Web应用程序时&#xff0c;将所有功能公开为RESTful API&#xff0c;然后自己使用它是一种最佳实践。 这通常与使用繁重的javascript的丰富前端配合使用&#xff0c;例如Angular / Ember / Backbone / React。 但是沉重的前端似乎不是一个很好的默认…

c语言怎样获得函数内参数的值_C语言可变参数函数的实现原理

在本人的《C语言可变参数函数的实现方法》一文中&#xff0c;介绍了如何建立自己的可变参数函数。下面继续介绍可变参数函数的实现原理。在汇编语言程序设计中&#xff0c;详细介绍了子程序的实现思想&#xff1a;(1)子程序只是一段代码的起始地址&#xff1b;(2)调用子程序之前…

Log4j使用详解(log4j.XML格式)——整理

log4j.xml配置详解&#xff08;原文地址&#xff09; 首先当然是得到log4j的jar档&#xff0c;推荐使用1.2.X版&#xff0c;下载地址&#xff1a; http://logging.apache.org/log4j/1.2/download.html xml格式的log4j配置文件概述 xml格式的log4j配置文件需要使用org.apache…

SAS

options nosource; * 抑制SAS语句日志;options nosource2; * 抑制宏到日志文件的打印;options notes; * 抑制所有消息记录;你也可以三个一起用&#xff0c;如下&#xff1a;options nosource nosource2 notes; 下面的是连错误都不打印 option nonotes nomprint nosource nos…

[Swift]LeetCode39. 组合总和 | Combination Sum

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号&#xff1a;山青咏芝&#xff08;shanqingyongzhi&#xff09;➤博客园地址&#xff1a;山青咏芝&#xff08;https://www.cnblogs.com/strengthen/&#xff09;➤GitHub地址&a…

eclipse 重构_Eclipse对类固醇的重构

eclipse 重构在上一篇有关常见Java违规的文章中 &#xff0c;我列出了Java开发人员容易犯的一系列错误。 在重构Java项目以解决这些违规问题的同时&#xff0c;我广泛使用Eclipse的重构功能来快速更改代码。 下面是这种重构技术的汇编。 1.在块级语句周围添加花括号 用{curly …

python中能够处理的最大整数是_实例讲解Python中整数的最大值输出

在Python中可以存储很大的值&#xff0c;如下面的Python示例程序&#xff1a;x 10000000000000000000000000000000000000000000;x x 1print (x)输出&#xff1a;10000000000000000000000000000000000000000001在Python中&#xff0c;整数的值不受位数的限制&#xff0c;可以…