小伙伴们,有没有遇到过程序突然崩溃,然后抛出一个OutOfMemoryError的异常?这就是我们俗称的OOM,也就是内存溢出。简单来说,就是你的Java应用想要的内存超过了JVM愿意给的极限,就会抛出这个错误。
那么为什么会出现OOM呢?一般都是由这些问题引起:
接下来,我们来聊聊Java OOM的三大经典场景以及解决方案,保证让你有所收获!👍
这是最常见的OOM场景了,发生在JVM试图分配对象空间时,却发现剩余的堆内存不足以存储新对象。
例如我们执行下面的代码,就可以模拟出堆内存OOM的场景:
// 创建大量对象导致堆内存溢出
public class HeapOOM {
static class OOMObject {
// 假设这里有一些属性
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while (true) {
list.add(new OOMObject()); // 不断创建对象并添加到list中
}
}
}
那么当出现线上应用OOM场景时,该如何解决呢?
分析方法通常有两种:
在线分析Java OOM(内存溢出)问题,通常涉及到监控运行中的Java应用,捕获内存溢出时的信息,分析堆转储(Heap Dump)文件,以及利用一些工具和命令来辅助定位问题。下面是一套详细的分析流程和命令,帮助你在线分析和解决Java OOM问题:
在Java应用启动命令中加入以下JVM参数,以确保在发生OOM时能自动生成堆转储文件:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/heapdump.hprof
这些参数的作用是:
-XX:+HeapDumpOnOutOfMemoryError
:指示JVM在遇到OOM错误时生成堆转储文件。-XX:HeapDumpPath
:指定堆转储文件的存储路径,可以自定义路径和文件名。使用jvisualvm
或jconsole
等工具可以实时监控Java应用的内存使用情况。这些工具可以帮助你了解内存消耗的趋势,从而预测和避免OOM的发生。
当应用抛出OOM并且根据上述设置生成了堆转储文件后,使用Heap Dump分析工具来分析这个文件。常用的工具有:
在MAT中打开Heap Dump文件,主要关注以下几点:
JDK提供了一些命令行工具,如jmap
,可以用来生成Heap Dump文件:
jmap -dump:live,format=b,file=heapdump.hprof <pid>
其中<pid>
是Java进程的ID。-dump:live
选项表示只转储活动对象,可以减小Heap Dump文件的大小。
最后,不要忽视应用的日志和抛出的异常信息。OOM之前的日志可能会提供一些导致内存溢出的操作或业务逻辑的线索。
离线分析Java OOM(OutOfMemoryError)通常是在问题发生后,通过分析JVM生成的堆转储(Heap Dump)文件来进行。这个过程涉及到获取堆转储文件、使用分析工具进行深入分析和解读分析结果
首先,确保你已经有了一个Heap Dump文件。这个文件可能是在JVM遇到OOM时自动生成的(如果启用了-XX:+HeapDumpOnOutOfMemoryError
JVM参数),或者你可以在应用运行期间手动生成:
jmap
命令生成Heap Dump文件: jmap -dump:live,format=b,file=/path/to/heapdump.hprof <pid>
其中<pid>
是Java进程的ID,/path/to/heapdump.hprof
是你希望保存Heap Dump文件的位置。
有了Heap Dump文件后,你需要使用专门的工具来进行分析。以下是一些常用的分析工具:
使用MAT(Eclipse Memory Analyzer)作为示例,分析流程如下:
下面给大家提供一份Java应用上线前参考的的JVM配置(内存8G),以后系统上线前可以先配置下JVM,不要啥都不配置就上线了
-Xms6g -Xmx6g (按不同容器,4G及以下建议为50%,6G以上,建议设置为70%)
-Xmn2g (以8G内存,年轻代可以设置为2G)
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
-Xss256k
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:AutoBoxCacheMax=20000
-XX:+HeapDumpOnOutOfMemoryError (当JVM发生OOM时,自动生成DUMP文件)
-XX:HeapDumpPath=/usr/local/logs/gc/
-XX:ErrorFile=/usr/local/logs/gc/hs_err_%p.log (当JVM发生崩溃时,自动生成错误日志)
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/usr/local/heap-dump/
Java元空间(Metaspace)是Java虚拟机(JVM)中用于存放类的元数据的区域,从Java 8开始引入,替代了之前的永久代(PermGen)
图中红色箭头所指就是元空间
元空间是方法区在HotSpot JVM
中的实现,方法区主要用于存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。
不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。理论上取决于32位/64位系统可虚拟的内存大小,可见也不是无限制的,需要配置参数。
元空间(Metaspace) 垃圾回收,会对僵死的类及类加载器的垃圾回收会进行回收,元空间(Metaspace) 垃圾回收的时机是,在元数据使用达到“MaxMetaspaceSize”参数的设定值时进行。
JVM 在启动后或者某个时间点开始,MetaSpace 的已使用大小在持续增长,同时每次 GC 也无法释放,调大 MetaSpace 空间也无法彻底解决。
比如:
例如下面的生成大量动态代理类的代码示例,则会导致元空间的OOM
// 使用CGLIB动态生成大量类导致元空间溢出
public class MetaspaceOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create(); // 动态生成类并加载
}
}
static class OOMObject {
// 这里可以是一些业务方法
}
}
Java对外内存(Direct Memory)OOM指的是Java直接使用的非堆内存(off-heap memory)耗尽导致的OutOfMemoryError。这部分内存主要用于Java NIO库,允许Java程序以更接近操作系统的方式管理内存,常用于高性能缓存、大型数据处理等场景
例如下面的代码,如何堆外内存太小,就会导致堆外内存的OOM:
// 分配大量直接内存导致OOM
import java.nio.ByteBuffer;
public class DirectMemoryOOM {
private static final int ONE_MB = 1024 * 1024;
public static void main(String[] args) {
int count = 1;
try {
while (true) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(ONE_MB);
count++;
}
} catch (Exception e) {
System.out.println("Exception: instance created " + count);
throw e;
}
}
}
-XX:MaxDirectMemorySize
参数控制对外内存大小,如果设置过小,可能无法满足应用需求。-XX:MaxDirectMemorySize
参数,给予足够的直接内存空间。sun.misc.Cleaner.clean()
或通过其他机制释放内存。最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。
这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软
本文,已收录于,我的技术网站 aijiangsir.com,有大厂完整面经,工作技术,架构师成长之路,等经验分享
点赞对我真的非常重要!在线求赞,加个关注我会非常感激!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。