异常结构图1
异常结构图2
Error错误:系统处理不了的
Exception异常
try-catch-finally 结构用于异常捕获和处理,而 throw 和 throws 关键字分别用于抛出异常和声明方法可能抛出的异常。
try {
// 可能抛出异常的代码
}
catch (ExceptionType1 e) {
// 处理ExceptionType1类型的异常
}
catch (ExceptionType2 e) {
// 处理ExceptionType2类型的异常
}
finally {
// 释放资源或其他必须执行的操作
}
if (someCondition) {
throw new SomeException("Error message");
}
public void someMethod() throws IOException, SQLException {
// 方法体
}
Error 是一个继承自 Throwable 类的特殊类,它表示系统级的错误或严重的资源耗尽错误,通常是不可恢复的情况。与受检异常(Checked Exception)和运行时异常(Unchecked Exception - 继承自 RuntimeException)不同,程序员通常无法预防或处理这些错误。
对于Error类型的异常,它们通常不是通过try-catch块来捕获并处理的,因为它们通常意味着应用程序无法继续正常运行,或者是JVM自身的问题。在实际开发中,更多关注的是如何避免触发这些严重错误以及对系统进行合理的监控和优化。
此类错误代表JVM内部错误,如内存溢出(OutOfMemoryError)或栈空间不足(StackOverflowError)等。
当一个线程被另一个线程中断时抛出,虽然不推荐直接使用。
这类错误发生在类加载器加载类的过程中,例如找不到类定义(NoClassDefFoundError)、类文件格式错误(ClassFormatError)或者静态初始化失败(IncompatibleClassChangeError)等。
在启用断言的情况下,如果一个断言语句的结果为假,则会抛出此错误,用于开发者调试程序逻辑。
Java虚拟机内部错误或条件出现异常情况,这种情况通常表明Java虚拟机本身存在bug。
是 Java 虚拟机(JVM)在运行时抛出的一种错误类型,它表明 JVM 已无法为应用程序分配更多内存。
Java堆空间溢出
Java对象主要存储在堆内存中。当新创建的对象过多,或者大对象占用的空间过大且生命周期较长,导致堆空间被占满,而垃圾回收器又无法及时回收足够的内存供新对象使用时,就会抛出 java.lang.OutOfMemoryError: Java heap space 错误。 解决方案:可以通过增加JVM的堆内存大小,通过调整 -Xms(初始堆大小)和 -Xmx(最大堆大小)参数;另外,还需要检查代码逻辑,是否存在不必要的对象创建或长期持有大量数据的情况,优化内存使用,提高垃圾回收效率。
方法区溢出(Metaspace 或 PermGen)
在Java 8及以后版本中,永久代已被元空间(Metaspace)替代,而在更早的版本中,类信息、常量池、静态变量等数据会存储在永久代(PermGen)。如果动态加载的类数量过多或者字符串常量过大,可能导致方法区内存不足。 解决方案:对于Java 8及以上版本,可通过 -XX:MaxMetaspaceSize 设置元空间的最大大小;对于Java 7及更低版本,可使用 -XX:PermSize 和 -XX:MaxPermSize 参数来限制永久代大小。同时,减少无用类的生成以及优化字符串常量使用也是必要的。
本地图像直方图缓存溢出
在处理大型图像或大量图像时,也可能因为本地缓冲区(如DirectBuffer)不足而导致 OOM。 解决方案:根据应用需求适当增加允许使用的本地图像缓冲区大小,或者优化对这类资源的管理与释放。
总之,在遇到 OutOfMemoryError 时,应当首先查看错误的具体信息,了解是哪部分内存区域溢出,然后结合程序逻辑和JVM监控工具(如JVisualVM、jmap等)分析内存使用情况,找出内存泄漏点或过度消耗内存的地方,进而采取针对性的解决方案。
OutOfMemoryError 是Java虚拟机(JVM)在内存分配失败时抛出的一个严重错误。当JVM无法再为应用程序分配所需的内存时,它会抛出此异常,并且通常会导致受影响的应用程序或服务停止正常工作。
对于一个长时间运行的服务来说,一旦出现 OutOfMemoryError,不仅当前请求可能无法完成,而且由于内存资源耗尽,其他正在执行的线程也可能会受到影响,导致系统整体性能急剧下降或者完全失效。虽然严格意义上说,它不会立即导致操作系统层面的“宕机”(即操作系统强制关闭),但对应用程序而言,其效果等同于宕机,因为应用程序将无法继续处理新的请求,甚至可能导致整个应用崩溃。
在生产环境中,遭遇 OutOfMemoryError 通常意味着服务需要被立刻重启以恢复功能,如果不及时处理,可能会导致严重的业务中断和服务不可用。解决这类问题的关键在于合理配置JVM内存大小、优化代码以减少不必要的内存消耗以及排查并修复潜在的内存泄漏问题。
现在大家的服务都是有监控的,产生了OOM,一般都是立即自动重启服务
是Java虚拟机(JVM)在运行时抛出的一种错误,它发生在Java程序的调用栈空间耗尽时。每个线程在执行方法调用时都会使用一个独立的调用栈来存储方法的局部变量、返回地址等信息。当递归调用过深或者在没有合理退出条件的情况下无限递归时,会持续占用栈空间,直到栈内存被占满为止,这时JVM就会抛出 java.lang.StackOverflowError。
总之,遇到 StackOverflowError 时,应重点分析程序中的递归调用部分,确保有合理的退出条件,并且根据实际情况评估和调整JVM的线程栈大小配置。
StackOverflowError 通常会导致受影响的线程停止执行,因为栈空间耗尽意味着无法继续进行方法调用和局部变量分配。对于单个线程来说,抛出 StackOverflowError 时,该线程会立即终止执行,并且错误会被抛出到异常处理机制中(如果没有捕获,则程序通常会崩溃)。
在多线程环境中,即使一个线程由于 StackOverflowError 崩溃,其他线程仍然可能继续运行,因此整个Java虚拟机(JVM)不一定会立即宕机或退出。但是,如果受影响的是应用程序的关键线程或者多个线程同时遭遇栈溢出问题,那么整个应用程序可能会变得不稳定,甚至可能导致整个服务不可用,这在实际效果上等同于宕机。
总之,虽然 StackOverflowError 不一定会直接导致整个系统的物理宕机(即操作系统层面的关闭),但它确实会导致Java应用出现严重故障,影响其正常功能,进而可能需要重启应用才能恢复服务。对于服务器端应用程序,这种错误状况应当被迅速识别并采取相应的恢复措施。
Java中的受检异常(Checked Exception)是指那些在编译阶段必须显式处理的异常。当一个方法可能会抛出受检异常时,开发者必须在该方法中使用 try-catch 块来捕获并处理这个异常,或者在方法签名上通过 throws 关键字声明该方法可能抛出的受检异常,将异常处理的责任传递给调用者。
受检异常通常用于表示程序运行时可能会遇到的、正常情况下可以预见并且应当被合理处理的情况,
要求开发人员在编写代码时显式处理。
Java 中的受检异常(Checked Exception)是指那些继承自 java.lang.Exception 类但不继承自 java.lang.RuntimeException 的所有异常类及其子类。这些异常在编译时必须被捕获或声明抛出,否则会导致编译错误。以下列出了一些常见的受检异常:
非受检异常(Unchecked Exception)主要包括那些继承自 java.lang.RuntimeException 类及其子类的异常。
非受检异常在编译阶段不会被检查,因此可以不用捕获或者在方法签名上声明。
然而,在编写代码时,仍然需要关注这些异常,并在适当的地方进行处理以确保程序健壮性。
示例
public static void main(String[] args) {
System.out.println(3 / 0);
}
执行情况
Exception in thread “main” java.lang.ArithmeticException: / by zero
方法a调用会声明抛出异常的方法b,方法a怎么做
方法a在方法声明处继续抛出异常
public class ExceptionDemo {
public static void main(String[] args) throws ClassNotFoundException {
m1();
}
private static void m1() throws ClassNotFoundException{
}
}
方法a捕获异常
public class ExceptionDemo {
public static void main(String[] args) {
try {
m1();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
private static void m1() throws ClassNotFoundException{
}
}
不能缺少try,可以只有catch或finally
catch主要用来处理异常发生时的情况,可以做重试操作、打印日志或者继续抛出异常
finally用来做最后一定会执行的事情,比如资源释放、关闭
在finally子句中的代码是最后执行的,并且是 一定会执行 的,即使try语句块中的代码出现了异常。
finally子句必须和try一起出现,不能单独编写。
在正常的异常处理流程和方法返回流程中,finally 块都会被执行。只有在上述特殊、极端的情况下,finally 块才可能不被执行。
如果在 try 或 catch 块中有 System.exit(int) 方法调用,并且该方法导致JVM退出,则finally块不会执行。
public static void main(String[] args) throws ClassNotFoundException{
try {
System.out.println("try...");
System.exit(0);
} finally {
System.out.println("finally...");
}
}
打印
当运行代码的线程被中断(如通过 Thread.interrupt()),并且该中断导致了虚拟机的退出或者线程的死亡,那么 finally 块可能无法执行。
在极少数情况下,如果 JVM 遇到严重的系统错误或资源耗尽(如 StackOverflowError 或 OutOfMemoryError),而这些错误使得 JVM 无法正常完成 finally 块的执行时,finally 块可能不会被执行。
若在 try 或 catch 块内出现了无限循环或死锁情况,使得控制流永远不能到达 finally 块,那么 finally 块当然也就无法执行。
总结来说,finally 块的执行时机是在方法即将返回前的一个固定步骤,无论是否有异常或 return 语句出现。这意味着在方法正常返回或因异常退出前,都会先执行完 finally 块中的代码。
子类重写的方法抛出的受检异常类型应与父类方法声明的异常相容,
即子类重写的方法可以不抛出异常,也可以抛出父类方法所抛出异常的子类