Java中的VirtualMachineError

目录

1. VirtualMachineError 的类型

1.1 OutOfMemoryError

1.2 StackOverflowError

1.3 InternalError

1.4 UnknownError

2. 特征

2.1 非受检异常

2.2 同步模式与异步模式


1. VirtualMachineError 的类型

有四种不同类型的 VirtualMachineError:

OutOfMemoryError
StackOverflowError
InternalError
UnknownError
让我们在本节中详细回顾这些类型。

Java throwable 类层次结构

1.1 OutOfMemoryError

OOM(OutOfMemoryError)在 DevOps 社区中非常流行。 虽然大多数 DevOps 的工程师可能认为只有一种 OutOfMemoryError,但实际上 OutOfMemoryError 有九种不同类型:

  • java.lang.OutOfMemoryError:在Java 堆空间中创建新的对象
  • java.lang.OutOfMemoryError:GC 开销超过限制
  • java.lang.OutOfMemoryError:请求的数组大小超过虚拟机限制,JVM 在为数组分配内存前,会检查要分配的数据结构在系统中是否可寻址,通常为 Integer.MAX_VALUE - 2
  • java.lang.OutOfMemoryError:Permgen 空间(Jdk8取消该区域)
  • java.lang.OutOfMemoryError:Metaspace被用满
  • java.lang.OutOfMemoryError:无法创建一个新的 native 线程
  • java.lang.OutOfMemoryError:杀死进程或子进程
  • java.lang.OutOfMemoryError:超出默认Direct ByteBuffer大小
  • java.lang.OutOfMemoryError:Out of swap space?

触发每种错误的原因各有不同。类似地,根据 OutOfMemoryError 不同的问题类型,对应的解决方案也不一样。查找OOM触发原因和解决方案,可以参考https://segmentfault.com/a/1190000019910501

通常可以通过分析垃圾回收日志和堆转储文件来诊断和修复 OutOfMemoryError 错误。手动分析垃圾回收日志可能会很乏味,可以考虑使用免费工具,如 GCeasy、HP Jmeter 或 IBM GC analyzer。 类似地,也可以考虑使用 HeapHero 或 Eclipse MAT 这样的免费工具来分析堆转储文件。

1.2 StackOverflowError

线程的堆栈存储了执行的方法、基本数据类型值、局部变量、对象指针和返回值信息,所有这些都会消耗内存。如果线程的堆栈大小超过了内存分配限制,就会抛出 java.lang.StackOverflowError。关于如何调试 StackOverflowError 的细节以及修复这个问题可能的解决方案,可以参考https://jaxenter.com/stackoverflowerror-causes-152027.html

出现StackOverflowError一般有以下两种情况:

  • 方法递归调用问题,导致栈帧不断增加,无法分配新的栈帧(栈容量最小值取决于操作系统内存分页大小)
  • 还有一种情况就是创建线程时,线程所需堆栈大于默认线程堆栈大小,这个时候需要调整参数-Xss<size>

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 可能在对象创建、数组创建、类初始化和装箱转换时同步或异步抛出。

<think>我们正在讨论Java中的错误类型体系分类,特别是Exception和Error的区别。根据用户提供的引用内容,我们可以总结如下: 1. Java异常体系结构:Throwable是Java语言中所有错误和异常的超类。它有两个直接子类:Error和Exception。 2. Error表示系统级错误,通常由JVM抛出,应用程序不应该捕获处理,因为这些错误通常表示严重问题,无法恢复。 3. Exception表示程序运行时可预料的问题,通常可以通过代码捕获并处理。 用户引用内容中关于Error和Exception的区别总结: - [^1]:异常处理是确保程序健壮性的机制,Exception和Error容易混淆。 - [^2]:面试题中常考察Exception和Error的区别,包括Java异常体系的理解、具体类型及处理方法等。 - [^3]:Error与Exception的深度对比,包括基本概念、典型场景和处理方式。 - [^4]:Error通常表示JVM无法恢复的问题,应用程序不应该处理;Exception分为checked exception(编译时异常)和unchecked exception(运行时异常)。 - [^5]:异常是程序正常运行中可能出现的问题,可以被捕获处理;错误是严重问题,通常无法处理。 因此,我们需要整理一个清晰的回答,包括: 1. Java错误类型体系结构(用类图展示) 2. Error和Exception的区别(表格对比) 3. 异常分类:checked exception和unchecked exception 4. 处理方式及最佳实践 下面开始组织回答:</think># Java错误类型体系:Exception与Error深度解析 ## 一、Java错误类型体系结构 Java的异常处理体系以`Throwable`为根类,形成完整的错误处理机制: ```mermaid classDiagram class Throwable { <<根类>> +String message +StackTraceElement[] stackTrace +getMessage() String +printStackTrace() void } class Error { <<系统级错误>> +VirtualMachineError +LinkageError +... } class Exception { <<程序级异常>> +IOException +SQLException +RuntimeException } Throwable <|-- Error Throwable <|-- Exception Exception <|-- RuntimeException ``` ### 1. Throwable核心属性 - **message**:错误/异常描述信息 - **stackTrace**:方法调用栈信息(关键调试依据) - **cause**:嵌套的异常原因(支持异常链) ## 二、Error与Exception核心区别 | **特性** | **Error** | **Exception** | |-------------------|------------------------------------|--------------------------------| | **产生来源** | JVM或底层系统资源问题 | 程序逻辑或外部资源问题 | | **可恢复性** | 通常不可恢复 | 多数可捕获处理 | | **处理必要性** | 不应捕获处理 | 必须捕获或声明抛出 | | **典型示例** | `OutOfMemoryError`,`StackOverflowError` | `NullPointerException`,`IOException` | | **处理方式** | 终止程序并记录日志 | 捕获后尝试恢复或优雅降级 | ## 三、Error类体系详解 ### 1. 虚拟机错误(VirtualMachineError) - **`OutOfMemoryError`**:堆内存耗尽 ```java // 触发示例 List<byte[]> list = new ArrayList<>(); while(true) { list.add(new byte[1024*1024]); // 持续分配1MB内存 } ``` - **`StackOverflowError`**:方法调用栈溢出 ```java void recursiveCall() { recursiveCall(); // 无限递归 } ``` ### 2. 链接错误(LinkageError) - **`NoClassDefFoundError`**:类文件存在但加载失败 - **`UnsatisfiedLinkError`**:JNI本地方法链接失败 > **关键原则**:应用程序不应捕获`Error`子类,也不应自定义`Error`[^4] ## 四、Exception类体系详解 ### 1. 受检异常(Checked Exceptions) - **特点**:编译时强制检查,必须处理 - **典型类型**: - `IOException`(文件操作) - `SQLException`(数据库操作) - `ClassNotFoundException`(类加载) ```java // 必须处理受检异常 try { Files.readString(Path.of("missing.txt")); } catch (IOException e) { // 必须捕获或声明抛出 System.err.println("文件读取失败: " + e.getMessage()); } ``` ### 2. 非受检异常(Unchecked Exceptions) - **特点**:编译时不强制处理 - **典型类型**: - `NullPointerException`(空指针) - `ArrayIndexOutOfBoundsException`(数组越界) - `IllegalArgumentException`(非法参数) ```java // 运行时异常示例 String text = null; try { System.out.println(text.length()); // 抛出NullPointerException } catch (RuntimeException e) { // 非强制捕获 System.out.println("捕获到运行时异常"); } ``` ## 五、异常处理最佳实践 ### 1. 异常处理金字塔原则 ```mermaid graph TD A[底层代码] --> B[抛出具体异常] B --> C[中层捕获转换] C --> D[顶层统一处理] D --> E[用户友好提示] ``` ### 2. 异常处理准则 - **DOs**: ```java // 精确捕获异常类型 try { // 可能抛出多种异常的代码 } catch (FileNotFoundException e) { // 处理特定异常 } catch (IOException e) { // 处理更通用的异常 } ``` - **DON'Ts**: ```java // 反模式:捕获所有异常但不处理 try { riskyOperation(); } catch (Throwable t) { // 过于宽泛 // 空catch块(吞没异常) } ``` ### 3. 自定义异常实践 ```java // 创建业务异常 public class PaymentException extends Exception { private final String transactionId; public PaymentException(String message, String transactionId) { super(message); this.transactionId = transactionId; } public String getTransactionId() { return transactionId; } } // 使用场景 void processPayment() throws PaymentException { if (paymentFailed) { throw new PaymentException("支付失败", "TX123456"); } } ``` ## 六、现代异常处理模式 ### 1. try-with-resources(Java 7+) ```java // 自动资源管理 try (InputStream is = new FileInputStream("data.bin"); OutputStream os = new FileOutputStream("backup.bin")) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } } // 自动调用close() ``` ### 2. 多异常捕获(Java 7+) ```java try { // 可能抛出多种异常 } catch (IOException | SQLException e) { // 合并捕获 logger.error("操作失败", e); throw new ServiceException(e); } ``` ## 七、性能考量 异常处理成本主要来自: 1. **栈轨迹生成**:`fillInStackTrace()`消耗较大 2. **异常对象创建**:频繁创建异常对象增加GC压力 3. **控制流跳转**:破坏局部性原理 > **优化建议**:在性能关键路径避免使用异常控制流程,改用状态码或Optional --- **黄金法则**: 1. 对可恢复情况使用**受检异常** 2. 对编程错误使用**运行时异常** 3. 对JVM严重问题**不捕获Error** 4. 避免在finally块中使用return(导致异常吞没) 5. 日志记录应包含完整堆栈和上下文信息[^2][^5]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值