1.java的优势是啥?
相比于 C 语言的手动释放内存,Java 的优势在于内存的自动管理,依赖于垃圾回收机制,它能自动识别和清理不再使用的内存资源,消除了手动释放内存的繁琐过程,大大简化了开发人员的工作量。
2. 什么是 OOM?
这个我在线上也碰到过好多次了,Java 的 OOM 通常指的是内存溢出(Out of Memory)异常。在 Java 应用程序中,每个对象都需要在内存中分配一定的空间。当应用程序需要分配更多内存空间来创建对象时,但可分配内存却不足以满足需求时,就会抛出 OOM 异常。
3.什么情况会产生 OOM
比如说经常发生的堆内存溢出, 在创建对象时,绝大多数情况占用的都是 JVM 的堆内存,当堆内存不足以分配时,则会抛出OOM异常。
java.lang.OutOfMemoryError: Java heap space
堆内存溢出的具体场景
常见导致内存溢出的情况有这么几种:
对象生命周期过长:如果某个对象的生命周期过长,而且该对象占用的内存很大,那么在不断创建新对象的过程中,堆内存会被耗尽,从而导致内存溢出。这种情况一般出现在用集合当缓存,却忽略了缓存的淘汰机制。
无限递归:递归调用中缺少退出条件或递归深度过大,会导致空间耗尽,引发溢出错误。往往在测试环境就会发现该问题,不会暴露在生产环境
大数据集合:在处理大量数据时,如果没有正确管理内存,例如加载过大的文件、查询结果集过大等,会导致内存溢出。
JVM配置不当:如果JVM的内存参数配置不合理,例如堆内存设置过小,无法满足应用程序的内存需求,也会导致内存溢出。
4.什么是内存泄漏
对象的引用未被正确释放:如果在使用完一个对象后,忘记将其引用置为 null 或者从数据结构中移除,那么该对象将无法被垃圾回收,导致内存泄漏。比如 ThreadLocal。
长生命周期的对象持有短生命周期对象的引用:如果一个长生命周期的对象持有了一个短生命周期对象的引用,即使短生命周期对象不再使用,由于长生命周期对象的引用仍然存在,短生命周期对象也无法被垃圾回收,从而造成内存泄漏。
过度使用第三方库:某些第三方库可能存在内存泄漏或者资源未正确释放的问题,如果使用不当或者没有适当地管理这些库,可能会导致内存溢出。
集合类使用不当:在使用集合类时,如果没有正确地清理元素,当集合不再需要时,集合中的对象也不会被释放,导致内存泄漏。
资源未正确释放:如果程序使用了诸如文件、数据库连接、网络连接等资源,在不再需要这些资源时没有正确释放,会导致资源泄漏,最终导致内存泄漏。
递归调用导致栈溢出
当递归调用的层级过深,栈空间无法容纳更多的方法调用信息时,会引发 StackOverflowError 异常,这也是一种 OOM 异常。例如,以下示例中的无限递归调用会导致栈溢出。
5.元空间(Metaspace)耗尽
类加载过多:如果应用程序动态加载大量的类或者使用动态生成类的方式,会导致元空间的使用量增加。如果无法及时卸载这些类,元空间可能会耗尽。
字符串常量过多:Java中的字符串常量会被存储在元空间中。如果应用程序中使用了大量的字符串常量,尤其是较长的字符串,可能会导致元空间的耗尽。
频繁使用反射:反射操作需要大量的元数据信息,会占用较多的元空间。如果应用程序频繁使用反射进行类的操作,可能会导致元空间耗尽。
大量动态代理:动态代理是一种使用反射创建代理对象的技术。如果应用程序大量使用动态代理,将会生成大量的代理类,占用较多的元空间。
未正确限制元空间大小:默认情况下,元空间的大小是不受限制的,它会根据需要动态扩展。如果没有正确设置元空间的大小限制,或者限制过小,可能会导致元空间耗尽。
6.当 Java 线程在处理请求时,抛出了 OOM 异常,整个进程还能处理请求吗?
内存溢出的情况,当 GC 的速度跟不上内存的分配时,会发生 OOM, 从而将那个线程 Kill 掉,在这种情况下,进程一般还能继续处理请求。
内存泄漏的情况,由于这些内存不能被回收掉,会发生OOM,从而将那个线程 Kill 掉,防止继续创建不能被回收的对象,此时有些不占用内存的线程可能将继续执行,而那些会占用大量内存的线程可能将无法执行,最坏的情况可能是进程直接挂掉。