无抛出 VirtualMachineError 保证

No-throw VirtualMachineError guarantees

提问人:Raedwald 提问时间:1/4/2012 最后编辑:Matthias BraunRaedwald 更新时间:6/11/2021 访问量:6376

问:

我是从C++开始学习Java的。在 C++ 世界中,我们关注异常安全,并注意到 mutator 在面对 mutator 本身或它委托给的方法(最小值、强值、不抛出)抛出的异常时,mutator 可以提供不同的保证。实现具有强异常保证的方法需要保证某些基本操作永远不会引发异常。JLS 会声明哪些操作可以抛出哪些类型的异常,但该错误会带来问题。引用 JLS:VirtualMachineError

内部错误或资源限制阻止了 Java 虚拟 机器从实现 Java 编程的语义 语言;在本例中,将抛出 的子类的实例。VirtualMachineError

JLS 不再说 .“内部错误”意味着 JVM 中的错误,所以我对这种情况不感兴趣:面对 JVM 中的错误,所有的赌注都失败了。但是“资源限制”的情况呢?是否有任何操作可以保证永远不会因资源限制而失败?VirtualMachineError

Java 异常 JVM 正确性

评论

0赞 Joop Eggen 1/4/2012
最接近答案的是 .当然,如果记忆耗尽,任何延续都将被证明是几乎不可能的。现在这在C++中没有什么不同。try { ... } catch (Throwable t) { }
2赞 Raedwald 1/4/2012
我在回答我自己的问题。FAQ甚至鼓励这样做。

答:

18赞 Raedwald 1/4/2012 #1

引用 Java 虚拟机规范

此规范无法预测内部错误或资源的位置 可能会遇到限制,并且没有确切规定何时 他们可以被报告。因此,任何子类 下面定义的可以在操作期间随时抛出 Java 虚拟机:VirtualMachineError

因此,在 Java 中,不能对 VirtualMachineError 异常做出任何异常保证。所有例外保证必须符合“......但如果抛出 A,则不会”。这是Java与C++的不同之处之一。VirtualMachineError

这也表明,捕获异常没有多大意义,因为如果抛出异常,则程序处于未定义状态。不幸的是,这包括例外情况。很不幸,因为如果一个程序有几个独立的任务要执行(例如,一个 Web 服务器),如果一个任务因为需要太多内存而失败,我们可能希望继续执行其他任务。VirtualMachineErrorOutOfMemoryError

评论

0赞 skaffman 1/4/2012
你是在回答你自己的问题,还是只是补充它?
2赞 Raedwald 1/4/2012
我正在回答我自己的问题。
0赞 Andy Thomas 1/4/2012
VirtualMachineError 和 OutOfMemoryError 都是错误。Java 不仅会崩溃,还会在某些异常情况下抛出错误。也就是说,大多数程序都不打算捕获任何 Error 子类。可以将错误与可能捕获的异常区分开来。Herb Sutter 提倡的异常保证也可以应用于 Java,用于普通异常。
0赞 Raedwald 1/10/2012
Смотритетакже: stackoverflow.com/questions/1692230/...
0赞 Raedwald 7/8/2013
Смотритетакже: stackoverflow.com/questions/3871278/...
1赞 kosa 1/4/2012 #2

如果是资源限制,首先,不会进行任何操作。这是 VirtualMachineError 完美示例的链接。虚拟机错误

此错误与 OutofMemoryError 不同,此时某些操作可能正在进行中。

评论

0赞 Raedwald 1/4/2012
“不进行任何操作”。这将是“强有力的例外保证”。但是没有这样的保证。
0赞 kosa 1/4/2012
如果我的解释是正确的,基于上面的例子,在启动 VM 之前,它正在检查定义的资源,如 -Xmx 参数,但它找不到定义的内存大小并引发错误。我同意你关于不做任何保证的观点,但不知道为什么。
1赞 pap 1/4/2012 #3

我看到你已经回答了你自己的问题,我可以理解为什么这对你来说有点惊讶,因为你来自严格的 C++ 背景。这只是托管内存(虚拟机)计算机的现实,它不仅限于 java。内存可能会耗尽,因为 JVM 受限于它可以使用的堆数量(可在 java 命令行上配置)。

在C++/机器代码世界中,有点类似,但不等价,在尝试对未分配或位于虚拟地址空间之外的内存进行寻址时,您将获得一个GENERAL_PROTECTION_FAULT(如果您使用的是 *NIX,则为 SEGMENTATION_FAULT)。面对这种情况,提供“强异常保证”同样困难,因为原因可能是代码中的错误或完全超出程序的控制范围。

评论

0赞 Raedwald 1/4/2012
SEGV 表示程序中存在错误。它在 Java 中的类比是 。NullPointerException
0赞 Raedwald 1/4/2012
我不明白这是托管虚拟机的一般功能。即使您没有使用或尝试加载类,也允许抛出 JVMS。OutOfMemoryErrornew
0赞 Peter Lawrey 1/4/2012
@Raedwald,只有在尝试引用 时才会得到一个 NPE。只有在尝试分配更多内存(或调用执行此操作的方法)并且 GC 已运行且释放的内存不足后,才会收到 OutOfMemoryError。null
1赞 Raedwald 1/4/2012
“只有在尝试分配更多内存后,您才会得到 OutOfMemoryError”这是直觉的意思:只有才能引起它。但 JVMS 不是这么说的:JVM 随时可能抛出它。
3赞 pap 1/5/2012
好吧,我们在这里讨论语义。OutOfMemoryError 可以随时抛出,这是正确的。OutOfMemoryError 最常见的情况之一是“超出 GC 开销限制”情况,当 GC 由于每次传递中无法释放足够的内存而消耗过多的 CPU 时间时,就会引发这种情况。什么是先有鸡还是先有蛋,“新”还是 GC?GC 触发是不确定的,但尝试分配内存(通过“新”)绝对是它运行的强烈动力,所以虽然“新”可能不是直接的近端原因,但它当然是根本原因。
0赞 Peter Lawrey 1/4/2012 #4

在 Java 中,您可以随时调用 Thread.stop() 或 stop(Throwable)。大多数错误都被认为是非常严重的,除非你真的知道自己在做什么,否则你不应该尝试处理它们。

开发服务器端 Java 应用程序已有 12 年了,我可以说我从未听说过有人担心抛出随机异常。我怀疑这不是您需要担心的 Java 问题。

您能否举例说明为什么您认为需要保证,因为可能有另一种方法可以解决问题?

评论

2赞 Raedwald 1/4/2012
例外是最有趣的情况。正如我在自己的回答中所说:很不幸,因为如果几个任务中的一个因为需要太多内存而失败,我们可能希望继续执行其他任务。但是,如果 JVM 可以抛出 ,我们就不能依赖我们的程序在捕获 .这使得 try-catch-discard 方法不可靠。OutOfMemoryErrorOutOfMemoryErrorOutOfMemoryError
0赞 Andy Thomas 1/4/2012
在“尝试处理它们”之前添加了缺少的“NOT”。
2赞 Peter Lawrey 1/4/2012
@Raedwald 你说得对,丢弃OOME是不可靠的。您要做的是定义您预计整个 JVM 失败的最大内存(而不仅仅是软限制),并避免执行可能失败的操作,因为您不知道它们使用了多少内存。在最坏的情况下,您需要使用一个单独的进程,该进程可以根据需要重新启动。(这也可以防止 JNI 库中的错误)
3赞 Hakanai 4/7/2014
@PeterLawrey 这种策略在 Google Chrome 等应用程序中非常受欢迎,在这些应用程序中,所有繁重的工作都是在单独的进程中完成的,这些进程会以某种方式呈现回真实窗口。如果有一种方便的方法可以在 Java 中做这种事情,那就太好了。正如今天在大多数应用程序中一样,GUI 由在单个进程中运行的 EDT 处理,如果 GUI 端的某些内容击中了 OOME(例如,您尝试对太大的表进行排序),您所能做的就是抓住它并希望它不会破坏其他线程做重要的事情。