当Java虚拟机遇到内部错误或资源限制时,抛出Java.lang.VirtualMachineError,从而阻止其运行。它是JVM用来防止整个应用程序崩溃的自我防御机制。在本文中,我们将讨论不同类型 VirtualMachineError,它们的特征,触发它们的原因以及解决它们的潜在解决方案。
VirtualMachineError错误类型
- OutOfMemoryError
- StackOverflowError
- InternalError
- UnknownError
OutOfMemoryError
大家熟知的OOM异常;大部分都已为OOM只有一种,就是内存分配完了。其实OOM可以细分为以下8种(大吃一惊吧),且看:
- java.lang.OutOfMemoryError: Java heap space // Java堆空间
- java.lang.OutOfMemoryError: GC Overhead limit exceeded // 超出GC开销限制
- java.lang.OutOfMemoryError: Requested array size exceeds VM limit // 请求的数组大小VM限制
- java.lang.OutOfMemoryError: Permgen space // Permgen空间
- java.lang.OutOfMemoryError: Metaspace // 元空间
- java.lang.OutOfMemoryError: Unable to create new native thread // 无法创建本地进程
- java.lang.OutOfMemoryError: Kill process or sacrifice child // 杀死进程或者子进程
- java.lang.OutOfMemoryError: reason stack_trace_with_native_method // stack_trace_with_native_method原因
每种类型都由不同的原因触发。下边简单总结各种OOM类型及其原因和解决办法;
NO. | OutOfMemoryError | 频率 | 原因 | 解决办法 |
---|---|---|---|---|
1 | Java heap space | * | 1. 无法在Java堆栈中分配对象 2. 拥塞增加 3. 无意中持有对象的引用(内存泄露),导致对象无法被垃圾回收器回收 4. 过度使用终结器(finalizers)。 终结器对象不立即执行垃圾回收的。 终结器由守护线程(为finalization终结请求队列提供服务的)执行。 有时终结者线程执行的速度跟不上终结队列增大的速度。 |
1. 增加堆大小’-Xmx’。 2. 修复应用程序中的内存泄漏。 |
2 | GC Overhead limit exceeded | * | 1. Java进程花费了98%以上的时间进行垃圾收集却恢复不到堆的2%大小(GC执行成本太大了) | 1. 增加堆大小’-Xmx’ 2. 可以关闭GC开销限制:’-XX:-UseGCOverheadLimit’ 3. 修复应用程序中的内存泄漏 |
3 | Requested array size exceeds VM limit | ** | 1. 应用尝试分配比最大堆大小还大的数组 | 1. 增加堆大小’-Xmx’ 2. 修复程序中分配如此大数组的bug |
4 | Permgen space | *** | 1. Permgen空间包含: a. 类的名称、属性和方法 b. 与类相关的的对象数组和类型数组 c. Just In Time编译器优化 当此空间空间不足时,将抛出此错误 |
1. 增大Permgen大小’-XX:MaxPermSize’ 2. 应用程序重新部署而不重新启动可能会导致这个问题。所以重启JVM。 |
5 | Metaspace | *** | 1. 从Java 8开始Metaspace取代Permgen。类元数据在本地内存中分配(简称元空间)。如果metaspace耗尽,则抛出此错误 | 1. 如果已经设置了’-XX:MaxMetaSpaceSize’,增大该值; 2. 删除’-XX:MaxMetsSpaceSize’设置; 3. 减小Java堆的大小,给MetaSpace腾出更多空间; 4. 为服务器分配更多物理内存; 5. 可能是程序中的bug,修理它。 |
6 | Unable to create new native thread | * | 1. 在本地创建线程,没有足够的内存来创建新线程。表明本地内存不足了 |
1.为机器分配更多内存; 2.减少Java堆空间; 3.修复应用程序中的线程泄漏; 4.增加操作系统级别的限制; ulimit -a 最大用户进程(-u)1800 5.使用-Xss参数减少线程堆栈大小 |
7 | Kill process or sacrifice child | * | 内核机制 - 系统内严重不足时,系统将杀死进程 | 1. 将进程迁移到不同的计算机。 2. 为机器添加更多内存 (与所有其他OOM错误不同,该错误不是有JVM触发的,而是由操作系统) |
8 | reason stack_trace_with_native_method | * | 1. 本机方法遇到分配失败 2.打印堆栈跟踪 |
1. 使用系统工具进行诊断分析 |
OutOfMemoryError 可以通过分析垃圾收集日志和堆转储来诊断和修复。由于手动分析垃圾收集日志可能很繁琐,因此您可以考虑使用,
和
分析器等免费工具。与分析堆转储类似,您可以考虑使用
,
等免费工具。
StackOverflowError
线程堆栈存储有关其执行的方法,原始数据类型值,局部变量,对象指针和返回值的信息。所有这些都消耗内存。如果线程堆栈大小超出分配的内存限制,则抛出java.lang.StackOverflowError。常见的的是:当线程由于执行程序中的错误而一次又一次地递归调用相同的函数时,通常会发生此问题。
InternalError
当遇到以下情况是,JVM会抛出java.lang.InternalError:
- 虚拟机实现错误
- 底层主机操作系统出现故障
- 硬件故障
内部错误很少遇见,要了解,可移步:Oracle的Java Bug数据库搜索“InternalError”字符串,了解该类型错误最新的报告情况。UnknownError
发生异常或错误时抛出java.lang.UnknownError,但Java虚拟机无法报告实际的异常或错误。实际环境中,很少遇到这种错误。同样,在Oracle Java Bug数据库中通过搜索“UnknownError”了解该类型错误最新的报告情况。
特点
VirtualMachineError 有几个主要特征:
- 不可检查的异常
- 同步和异步传递
我们将在本节讨论这两个特征。
不可检查的异常
在编译时检查的异常称为可检查。如果代码中的某些方法抛出了已检查的异常,则该方法必须处理异常,或者必须使用throws关键字抛出异常。常见可检查异常的例子有IOException、SQLException、DataAccessException、ClassNotFoundException 等。
未经检查的异常没有此要求。他们不必被抓或被抛出。所有类型 VirtualMachineError 都是不可检查和捕获的异常。
同步和异步传递
异常可以通过以下两种方式抛出:
- 同步
- 异步
同步异常指的是,无论程序在类似的环境中执行多少次,都发生在特定的语句中,如NullPointerException、ArrayIndexOutOfBoundException等;
异步异常则可以在任何时间点发生,并且可以再程序语句的任何部分发生。在抛出的地方没有一致性。除了所有VirtualMachineErrors基本都是都是异步抛出。StackOverflowError由于本机方法执行或者Java虚拟机资源显示,可能会通过方法调用以及异步方法同步抛出;类似的还有OutOfMemoryError 可以在对象创建,数组创建,类初始化和装箱转换期间以同步方式抛出。