经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
工作中最常见的6种OOM问题
来源:cnblogs  作者:苏三说技术  时间:2024/3/18 15:04:30  对本文有异议

前言

最近我写的几篇线上问题相关的文章:《糟糕,CPU100%了》《如何防止被恶意刷接口》《我调用第三方接口遇到的13大坑》发表之后,在全网广受好评。

今天接着线上问题这个话题,跟大家一起聊聊线上服务出现OOM问题的6种场景,希望对你会有所帮助。

1 堆内存OOM

堆内存OOM是最常见的OOM了。

出现堆内存OOM问题的异常信息如下:

  1. java.lang.OutOfMemoryError: Java heap space

此OOM是由于JVM中heap的最大值,已经不能满足需求了。

举个例子:

  1. public class HeapOOMTest {
  2. public static void main(String[] args) {
  3. List<HeapOOMTest> list = Lists.newArrayList();
  4. while (true) {
  5. list.add(new HeapOOMTest());
  6. }
  7. }
  8. }

这里创建了一个list集合,在一个死循环中不停往里面添加对象。

执行结果:

出现了java.lang.OutOfMemoryError: Java heap space的堆内存溢出。

很多时候,excel一次导出大量的数据,获取在程序中一次性查询的数据太多,都可能会出现这种OOM问题。

我们在日常工作中一定要避免这种情况。

2 栈内存OOM

有时候,我们的业务系统创建了太多的线程,可能会导致栈内存OOM。

出现堆内存OOM问题的异常信息如下:

  1. java.lang.OutOfMemoryError: unable to create new native thread

给大家举个例子:

  1. public class StackOOMTest {
  2. public static void main(String[] args) {
  3. while (true) {
  4. new Thread().start();
  5. }
  6. }
  7. }

使用一个死循环不停创建线程,导致系统产生了大量的线程。

执行结果:

如果实际工作中,出现这个问题,一般是由于创建的线程太多,或者设置的单个线程占用内存空间太大导致的。

建议在日常工作中,多用线程池,少自己创建线程,防止出现这个OOM。

3 栈内存溢出

我们在业务代码中可能会经常写一些递归调用,如果递归的深度超过了JVM允许的最大深度,可能会出现栈内存溢出问题。

出现栈内存溢出问题的异常信息如下:

  1. java.lang.StackOverflowError

例如:

  1. public class StackFlowTest {
  2. public static void main(String[] args) {
  3. doSamething();
  4. }
  5. private static void doSamething() {
  6. doSamething();
  7. }
  8. }

执行结果:

出现了java.lang.StackOverflowError栈溢出的错误。

我们在写递归代码时,一定要考虑递归深度。即使是使用parentId一层层往上找的逻辑,也最好加一个参数控制递归深度。防止因为数据问题导致无限递归的情况,比如:id和parentId的值相等。

4 直接内存OOM

直接内存不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。

它来源于NIO,通过存在堆中的DirectByteBuffer操作Native内存,是属于堆外内存,可以直接向系统申请的内存空间。

出现直接内存OOM问题时异常信息如下:

  1. java.lang.OutOfMemoryError: Direct buffer memory

例如下面这样的:

  1. public class DirectOOMTest {
  2. private static final int BUFFER = 1024 * 1024 * 20;
  3. public static void main(String[] args) {
  4. ArrayList<ByteBuffer> list = new ArrayList<>();
  5. int count = 0;
  6. try {
  7. while (true) {
  8. // 使用直接内存
  9. ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
  10. list.add(byteBuffer);
  11. count++;
  12. try {
  13. Thread.sleep(100);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. } finally {
  19. System.out.println(count);
  20. }
  21. }
  22. }

执行结果:

会看到报出来java.lang.OutOfMemoryError: Direct buffer memory直接内存空间不足的异常。

5 GC OOM

GC OOM是由于JVM在GC时,对象过多,导致内存溢出,建议调整GC的策略。

出现GC OOM问题时异常信息如下:

  1. java.lang.OutOfMemoryError: GC overhead limit exceeded

为了方便测试,我先将idea中的最大和最小堆大小都设置成10M:

  1. -Xmx10m -Xms10m

例如下面这个例子:

  1. public class GCOverheadOOM {
  2. public static void main(String[] args) {
  3. ExecutorService executor = Executors.newFixedThreadPool(5);
  4. for (int i = 0; i < Integer.MAX_VALUE; i++) {
  5. executor.execute(() -> {
  6. try {
  7. Thread.sleep(10000);
  8. } catch (InterruptedException e) {
  9. }
  10. });
  11. }
  12. }
  13. }

执行结果:

出现这个问题是由于JVM在GC的时候,对象太多,就会报这个错误。

我们需要改变GC的策略。

在老代80%时就是开始GC,并且将-XX:SurvivorRatio(-XX:SurvivorRatio=8)和-XX:NewRatio(-XX:NewRatio=4)设置的更合理。

最近就业形式比较困难,为了感谢各位小伙伴对苏三一直以来的支持,我特地创建了一些工作内推群, 看看能不能帮助到大家。

你可以在群里发布招聘信息,也可以内推工作,也可以在群里投递简历找工作,也可以在群里交流面试或者工作的话题。

进群方式

添加苏三的私人微信:su_san_java,备注:博客园+所在城市,即可加入。

6 元空间OOM

JDK8之后使用Metaspace来代替永久代,Metaspace是方法区在HotSpot中的实现。

Metaspace不在虚拟机内存中,而是使用本地内存也就是在JDK8中的ClassMetadata,被存储在叫做Metaspace的native memory。

出现元空间OOM问题时异常信息如下:

  1. java.lang.OutOfMemoryError: Metaspace

为了方便测试,我修改一下idea中的JVM参数,增加下面的配置:

  1. -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

指定了元空间和最大元空间都是10M。

接下来,看看下面这个例子:

  1. public class MetaspaceOOMTest {
  2. static class OOM {
  3. }
  4. public static void main(String[] args) {
  5. int i = 0;
  6. try {
  7. while (true) {
  8. i++;
  9. Enhancer enhancer = new Enhancer();
  10. enhancer.setSuperclass(OOM.class);
  11. enhancer.setUseCache(false);
  12. enhancer.setCallback(new MethodInterceptor() {
  13. @Override
  14. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  15. return methodProxy.invokeSuper(o, args);
  16. }
  17. });
  18. enhancer.create();
  19. }
  20. } catch (Throwable e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }

执行结果:

程序最后会报java.lang.OutOfMemoryError: Metaspace的元空间OOM。

这个问题一般是由于加载到内存中的类太多,或者类的体积太大导致的。

好了,今天的内容先分享到这里,下一篇文章重点给大家讲讲,如何用工具定位OOM问题,敬请期待。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。

原文链接:https://www.cnblogs.com/12lisu/p/18079649

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号