即使是最好的代码也会抛出错误,对开发人员而言这意味着需要知道如何修复它们。在这篇文章中,Ram Lakshmanan 介绍了许多不同类型的 VirtualMachineError 以及如何解决这些问题,在应用程序不崩溃的前提下回到正常状态。
当 Java 虚拟机遇到内部错误或资源限制无法运行时,就会抛出 Java.lang.VirtualMachineError。它是 JVM 的一种自我防御机制,用于防止整个应用程序崩溃。 在本文中,我将讨论许多不同类型的 VirtualMachineError,它们各自的特点、各种触发原因、以及几种可能的修复方案。
1. VirtualMachineError 的类型
有四种不同类型的 VirtualMachineError:
-
OutOfMemoryError
-
StackOverflowError
-
InternalError
-
UnknownError
让我们在本节中详细回顾这些类型。
Java throwable 类层次结构
1.1 OutOfMemoryError
就像 OMG(Oh My God)的首字母缩写一样,OOM(OutOfMemoryError)在 DevOps 社区中非常流行。 虽然大多数 DevOps 的工程师可能认为只有一种 OutOfMemoryError,但实际上 OutOfMemoryError 有八种不同类型:
-
java.lang.OutOfMemoryError:Java 堆空间
-
java.lang.OutOfMemoryError:GC 开销超过限制
-
java.lang.OutOfMemoryError:请求的数组大小超过虚拟机限制
-
java.lang.OutOfMemoryError:Permgen 空间
-
java.lang.OutOfMemoryError:Metaspace
-
java.lang.OutOfMemoryError:无法新建本机线程
-
java.lang.OutOfMemoryError:杀死进程或子进
-
java.lang.OutOfMemoryError:发生 stack_trace_with_native_method
触发每种错误的原因各有不同。类似地,根据 OutOfMemoryError 不同的问题类型,对应的解决方案也不一样。这里有一份很好的文档,用一页纸总结了所有不同类型的 OutOfMemoryError、触发原因和解决方案。
通常可以通过分析垃圾回收日志和堆转储文件来诊断和修复 OutOfMemoryError 错误。手动分析垃圾回收日志可能会很乏味,可以考虑使用免费工具,如 GCeasy、HP Jmeter 或 IBM GC analyzer。 类似地,也可以考虑使用 HeapHero 或 Eclipse MAT 这样的免费工具来分析堆转储文件。
参见:StackOverFlowError:原因及解决方案
1.2 StackOverflowError
线程的堆栈存储了执行的方法、基本数据类型值、局部变量、对象指针和返回值信息,所有这些都会消耗内存。如果线程的堆栈大小超过了内存分配限制,那么就会抛出 java.lang.StackOverflowError。通常由于执行程序中有一个错误,在线程重复递归调用同一个函数时会发生这个问题。关于如何调试 StackOverflowError 的细节以及修复这个问题可能的解决方案,更多信息可以看这里。
1.3 InternalError
JVM 抛出 java.lang.InternalError 有三个原因,虚拟机软件出现错误、系统软件底层出现错误或者硬件出现故障。
然而,很少会遇到 InternalError 这样的错误。要了解哪些特定情况可能导致 InternalError,请在 Oracle 的 Java Bug 数据库 中搜索 InternalError。在写这篇文章的时候(2018年12月20日),Oracle Java Bug 数据库中仅报告了200个 InternalError,而且大多数都已经修复了,所以不必对此过于担心。
1.4 UnknownError
当发生异常或错误,但 Java 虚拟机无法报告确切的异常或错误时,就会抛出 java.lang.UnknownError。UnknownError 很少出现。事实上,在 Oracle Java Bug 数据库中搜索 UnknownError 时,只找到了2个 Bug。
参见:远程调试 Java 应用程序
2. 特征
VirtualMachineError 有两个主要特征:
-
非受检异常(Unchecked exceptions)
-
同步模式与异步模式
让我们在本节中讨论这两个特征。
2.1 非受检异常
有两种异常类型:受检异常和非受检异常。
在编译时检查的异常称为受检异常。如果代码中的某些方法抛出受检异常,那么该方法必须处理该异常或者使用 throws 关键字指定异常。受检异常包括 IOException、SQLException、DataAccessException、ClassNotFoundException 等。
非受检异常常没有这个要求,它们不需要捕获或者声明抛出。所有类型的 VirtualMachineError 都是非受检异常。
2.2 同步模式与异步模式
可以在两种模式下抛出异常:同步模式和异步模式。
同步异常在特定程序语句执行时发生,无论该程序在类似的环境中执行了多少次。同步异常的例子有 NullPointerException、 ArrayIndexOutOfBoundException 等。
异步异常可以在任何时间点和程序语句的任何部分发生,异常抛出的地方也不一样。所有的 VirtualMachineError 都是异步抛出的,但有时也会同步抛出。StackOverflowError 可能随方法调用而同步抛出,也可能随着本地方法执行或 Java 虚拟机资源限制异步抛出。类似地,OutOfMemoryError 可能在对象创建、数组创建、类初始化和装箱转换时同步或异步抛出。
欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 854393687
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!