我听说过,如果可能的话,你应该避免尝试使用catch块,因为它们价格昂贵。
我的问题是关于.NET平台:为什么尝试块很昂贵?
回复摘要:
在这个问题上显然有两个阵营:那些说试试块很贵的人,以及那些说“可能只是一点点”的阵营。
那些说try块很贵的人通常会提到解除调用堆栈的“高成本”。就个人而言,我不相信这个论点 - 特别是在阅读了如何存储异常处理程序之后here。
我发现有一篇文章非常有趣:它谈到了try块的“其他”性能影响(不一定是内存或CPU消耗)。 Peter Ritchie提到他发现try块中的代码没有被优化,因为它本来是编译器。你可以阅读他的发现here。
最后,有一篇博客文章介绍了在CLR中实现异常的人的问题。去看看Chris Brumme的文章here。
答案 0 :(得分:20)
块本身并不昂贵,而且它本身并不是捕获一个异常本身就是昂贵的,它是运行时展开调用堆栈直到它找到一个可以处理异常的堆栈帧。抛出异常是非常轻量级的,但是如果运行时必须向上走6个堆栈帧(即深度为6个方法调用)以找到适当的异常处理程序,可能会执行finally块,因此可能会看到明显的时间量
答案 1 :(得分:12)
您不应该避免使用try / catch块,因为这通常意味着您没有正确处理可能发生的异常。结构化异常处理(SEH)仅在实际发生异常时才很昂贵,因为运行时必须遍历调用堆栈以查找catch处理程序,执行该处理程序(并且可能有多个),然后执行finally块,然后返回控制回到正确位置的代码。
异常不是用于控制程序逻辑,而是用于指示错误条件。
最大的误解之一 关于例外的是它们的用途 “特殊情况。”现实 是他们的沟通 错误条件。来自一个框架 设计角度来看,没有这样的 作为“特殊情况”的事情。 条件是否异常或 不取决于使用环境, ---但是可重用的库很少知道如何使用它们。例如, OutOfMemoryException可能是 简单的数据输入 应用;它不是那么特别 对于自己做的应用程序 内存管理(例如SQL服务器)。 换句话说,一个人是特殊的 病情是另一个男人的慢性病 条件。 [http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx]
答案 2 :(得分:5)
尝试块根本不贵。除非引发异常,否则很少或不产生任何费用。如果抛出异常,那就是异常情况,你不再关心性能了。如果您的程序需要0.001秒或1.0秒才能倒下,这是否重要?不,不是的。重要的是报告给你的信息有多好,这样你就可以修复它并阻止它再次发生。
答案 3 :(得分:4)
我认为人们真的高估了抛出异常的性能成本。是的,有一个性能损失,但它相对较小。
我进行了以下测试,投掷并捕获了一百万例外。我的英特尔Core 2 Duo,2.8 GHz需要大约20秒。这是每秒约50K例外。如果你投入的只是其中的一小部分,那么你就会遇到一些架构问题。
这是我的代码:
using System;
using System.Diagnostics;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
{
try
{
throw new Exception();
}
catch {}
}
Console.WriteLine(sw.ElapsedMilliseconds);
Console.Read();
}
}
}
答案 4 :(得分:3)
当您将代码包装在try / catch块中时,编译器会发出更多IL;看,对于以下程序:
using System;
public class Program
{
static void Main(string[] args)
{
Console.WriteLine("abc");
}
}
编译器将发出此IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "abc"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Program::Main
对于略微修改的版本:
using System;
public class Program
{
static void Main(string[] args)
{
try { Console.WriteLine("abc"); }
catch { }
}
}
发出更多:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 23 (0x17)
.maxstack 1
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldstr "abc"
IL_0007: call void [mscorlib]System.Console::WriteLine(string)
IL_000c: nop
IL_000d: nop
IL_000e: leave.s IL_0015
} // end .try
catch [mscorlib]System.Object
{
IL_0010: pop
IL_0011: nop
IL_0012: nop
IL_0013: leave.s IL_0015
} // end handler
IL_0015: nop
IL_0016: ret
} // end of method Program::Main
所有这些NOP和其他费用。
答案 5 :(得分:3)
底线是你的添加代码,大概是有原因的。如果代码行没有产生一些开销,即使它的1个CPU周期,那么它为什么会存在呢?没有什么是免费的。
明智的做法,就像你添加到你的应用程序中的任何代码行一样,只要你需要它就可以把它放在那里。如果你需要做一些异常捕获,那就去做吧......就像你需要一个字符串来存储东西一样,创建一个新的字符串。通过相同的方法,如果声明一个未使用的变量,则会浪费内存和CPU周期来创建它,并且应该将其删除。与try / catch相同。
换句话说,如果有代码可以做某事,那么假设做某事会以某种方式消耗CPU和/或内存。
答案 6 :(得分:2)
这不是尝试阻止你需要担心 catch 块。然后,并不是你想要避免编写块:这是你想要尽可能多地编写永远不会实际使用它们的代码。
答案 7 :(得分:1)
我怀疑它们是否特别昂贵。很多时候,它们是必要/必需的。
虽然我强烈建议仅在必要时使用它们并在正确的位置/嵌套级别使用它们,而不是在每次调用返回时重新抛出异常。
我认为这个建议的主要原因是说你不应该使用try-catches if if else是一种更好的方法。
答案 8 :(得分:1)
这不是我会担心的事情。我宁愿关心尝试的清晰度和安全性......最终还是会对自己的“昂贵”感到不满。
我个人不使用286,也没有人使用.NET或Java。继续。担心编写会影响您的用户和其他开发人员的良好代码,而不是使用它的99.999999%正常工作的底层框架。
这可能不是很有帮助,我并不是说要严厉,而只是强调观点。
答案 9 :(得分:0)
略有O / T,但是......
有一个相当不错的设计概念,说你永远不应该要求异常处理。这意味着您应该能够在任何可能引发异常的条件之前查询任何对象。
就像在“write()”之前能说“writable()”一样,就像那样。
这是一个不错的主意,如果使用它,它会使Java中的检查异常看起来很愚蠢 - 我的意思是,检查一个条件,然后在那之后,被迫仍然为同样的条件写一个try / catch? / p>
这是一个非常好的模式,但是检查的异常可以由编译器强制执行,这些检查不能。此外,并非所有库都是使用此设计模式制作的 - 在您考虑异常时,请记住这一点。
答案 10 :(得分:0)
每次尝试都需要记录大量信息,例如堆栈指针,CPU寄存器的值等等,因此它可以解除堆栈并恢复传递 try 块时的状态,以防抛出异常。不仅每个尝试都需要记录大量信息,当抛出异常时,需要恢复很多值。所以尝试非常昂贵, throw / catch 也非常昂贵。
这并不意味着您不应该使用异常,但是,在性能关键代码中,您可能不应该使用太多尝试而且也不会经常抛出异常。