关于OOM面试详解

一、什么是OOM?
    当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出的Out  of  memory异常.

Out  of  memory从名字上就可以理解,就是内存不够或者耗尽.在安卓当中,我们知道,安卓系统会为每一个APP分配一个独立的工作空间,也就是我们知道的Dalvik虚拟机空间.这样每个App都可以运行在独立的空间上,而不受其它App的影响,但是我们要知道,安卓系统中的系统为每一个Dalvik虚拟机设定了一个最大内存限制,如果,当我们当前占用的内存加上我们申请的内存资源超过了这个限制,系统就会抛出OOM错误,OOM错误在项目开发过程中经常遇到的就是BITMAP,在大图加载的时候会出现,所以说大部分的OOM问题都是和bitmap加载有关.

二、一些容易混淆的概念
     内存溢出
就是我们之前说的out  of  memory,当我们当前占用的内存加上我们申请的内存资源超过了这个限制,系统就会抛出OOM错误

     2.     内存抖动

内存抖动是因为我们在短时间内,大量的对象被创建,然后马上释放,瞬间产生的对象会严重占用内存区域.当达到一定程度,就会触发GC,(gc就是垃圾回收),回收掉你这个对象,这样你刚刚产生的对象很快就会被回收.每次分配对象占用了很少的内存,但是它们叠加在一起就会造成堆内存的压力,从而触发更多的GC,这就是内存抖动的意义.

     3.    内存泄漏

内存不在GC掌握之内了.

那么内存泄漏根本原因是GC垃圾回收机制漏掉的垃圾对象无法回收,

从而导致该垃圾对象持有的内存持续占有,这就是内存泄漏.

当内存泄漏累计到一定程度,就会造成内存溢出的现象

这三者之间,最严重的就是OOM,在开发过程当中,一提到内存,第一个映入脑海的就是OOM,其次才是内存泄漏,最后才是内存抖动.内存抖动较前两者较轻.内存溢出可以这样理解,堆内存上有些内存没有被释放从而会失去控制造成程序使用的内存越来越少,导致系统运行速度减慢,严重情况下,oom他会造成整个程序的奔溃.所以说为了提高我们APP的质量,提高用户体验,我们必须找到解决oom的办法.

三、如何解决oom
可以大致分为其它两点

1.有关于bitmap的优化
A.图片的显示
我们加载合适尺寸的图片,当显示缩略图的时候,不要去网络请求加载大图,这是一种优化机制,比如说在ListView的时候,我们会监听滑动事件,在滑动的时候,我们不去调用网络请求,而只有监听到ListView滑动停止的时候,我们再去加载大图,把图片显示到image view上

B.及时释放内存
我们知道安卓系统是有自己的回收机制的,也就是java的回收机制,他可以不定期的回收掉不使用的内存空间,大家注意,它是不定期的,你不能指定一个时间段,去回收那个内存,就是说他回收的时候肯定会有Bitmap的那个控件,那为什么我们要及时的释放内存呢?因为我们知道bitmap的构造方法都是私有的,它是通过一个BitmapFactory这个类来实例化一个bitmap的,BitmapFactory所有的生成的bitmap对象都是通过JNI这个调用方式实现的,所以说加载bitmap到内存以后,它是包含两部分内存区域的,简单的来说一部分就是Java区,一部分就是C区,而这个bitmap对象,它是由Java部分分配的,不用的时候,它当然会用Java的回收机制回收掉了,而对应的那块C的内存区域,虚拟机是不能直接回收掉的,这个只能调用底层功能来释放.所以说,我们这里来释放内存就是释放C区域的内存.

C.图片压缩
我们在开始的时候,有可能加载一张很大的大图,大图直接超过了内存分配的大小,这样肯定会导致内存溢出,这时候,我们就需要对加载Bitmap进行控制,也就是图片压缩,图片压缩有很多种例如(质量压缩,比例压缩等,在这里我说的是比例压缩),而在进行图片进行压缩的时候,需要用到一个inSampleSize 属性,就是当把图片加载到内存之前,我们需要计算一个合适的缩放比例,避免不必要的大图载入,

D.inBitmap属性
我们可以使用bitmap的inBitmap这个属性,它可以提高安卓系统在bitmap分配和释放的执行效率.

E.捕获异常
在安卓系统中,我们知道在读位图,bitmap的时候,它分给虚拟机中的图片堆栈大小,它是有限制的,所以说,为了避免应用在分配bitmap内存中出现oom异常,所以说,我们在实例化bitmap的时候,一定要对oom异常进行异常的捕获.

2.其它方面
listView:convertview/lru
我们需要使用convertview 的复用,同时对于listview中的大图控件需要使用lru机制来进行bitmap,(三级缓存机制)

避免在onDraw方法里面执行对象的创建.
我们知道,如果你频繁的在onDraw这个方法中,频繁的调用创建对象的方法,你就会使内存突然上升,这样,你在释放内存的时候,你会造成频繁的GC,这样就会造成前面说的内存抖动现象,内存抖动现象积累到一定程度也会造成oom.

谨慎使用多进程
多进程就是可以把应用中的部分组件运行在单独的进程当中,比如说App当中的定位,Webview也可以开启一个进程避免内存泄漏,

可以扩大内存占用的范围,就是说你开启了其它进程就不用占用主进程的内存,但是要注意谨慎使用,如果你的App真的没有大到一定程度,尽量少用多进程,一方面是牵扯到多进程代码更加复杂,逻辑更加的繁琐,但使用不当,不仅仅是造成内存增长,也会造成其它莫名其妙的问题.当你的应用一定需要一个后台常驻任务,你一定要开启一个进程,那么你可以考虑这个技术.

 

 

 

 

自我总结:
在 Java 面试中,OOM(Out of Memory,内存溢出)是一个常见的考点。下面将详细介绍几种常见的 Java OOM 情况,包括原因、示例代码和解决办法。

1. Java 堆溢出(Java Heap Space)

原因

Java 堆是用于存储对象实例的区域。当不断创建新对象,且这些对象无法被垃圾回收机制回收时,堆内存会被耗尽,从而导致堆溢出。常见的原因有:

  • 内存泄漏:对象已经不再使用,但由于某些引用的存在,垃圾回收器无法回收它们。
  • 大对象创建:一次性创建了非常大的对象,超出了堆内存的承受能力。

示例代码

import java.util.ArrayList;
import java.util.List;

public class HeapOOMExample {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        while (true) {
            // 不断创建 1MB 大小的字节数组
            list.add(new byte[1024 * 1024]);
        }
    }
}

解决办法

  • 检查代码中是否存在内存泄漏,确保不再使用的对象及时释放引用。
  • 调整堆内存大小,可以通过 -Xmx-Xms 参数来设置最大堆内存和初始堆内存。例如,java -Xmx512m -Xms256m HeapOOMExample 表示将最大堆内存设置为 512MB,初始堆内存设置为 256MB。

2. 栈溢出(StackOverflowError)

原因

Java 栈用于存储方法调用的栈帧,每个方法调用都会在栈上创建一个栈帧。当方法调用层次过深,栈空间被耗尽时,就会抛出 StackOverflowError。常见的原因是递归调用没有正确的终止条件。

示例代码

public class StackOverflowExample {
    public static void recursiveMethod() {
        recursiveMethod(); // 递归调用自身
    }

    public static void main(String[] args) {
        recursiveMethod();
    }
}

解决办法

  • 检查递归方法,确保有正确的终止条件。
  • 可以通过 -Xss 参数调整栈内存大小,例如 java -Xss2m StackOverflowExample 表示将栈内存大小设置为 2MB。

3. 方法区溢出(Metaspace 或 PermGen 空间)

原因

在 Java 8 之前,方法区使用永久代(PermGen)实现,用于存储类的元数据信息;Java 8 及以后,使用元空间(Metaspace)替代了永久代。当不断加载新的类,且元空间或永久代的内存不足时,就会导致溢出。常见的原因有:

  • 动态生成大量的类,例如使用 CGLIB 或反射机制。
  • 类加载器泄漏,导致类的元数据无法被卸载。

示例代码(Java 8 之前使用永久代)

import java.lang.reflect.Proxy;

public class PermGenOOMExample {
    public static void main(String[] args) {
        while (true) {
            Proxy.newProxyInstance(PermGenOOMExample.class.getClassLoader(),
                    new Class<?>[]{Runnable.class},
                    (proxy, method, args1) -> null);
        }
    }
}

解决办法

  • 检查代码中是否存在动态生成大量类的情况,尽量减少不必要的类加载。
  • 对于 Java 8 之前的版本,可以通过 -XX:MaxPermSize 参数调整永久代的大小;对于 Java 8 及以后的版本,可以通过 -XX:MetaspaceSize-XX:MaxMetaspaceSize 参数调整元空间的大小。

4. 直接内存溢出(Direct Memory)

原因

直接内存不是 Java 堆的一部分,它通过 Unsafe 类或 ByteBuffer.allocateDirect() 方法分配。当直接内存分配过多,且没有及时释放时,会导致直接内存溢出。

示例代码

import java.nio.ByteBuffer;

public class DirectMemoryOOMExample {
    public static void main(String[] args) {
        while (true) {
            ByteBuffer.allocateDirect(1024 * 1024);
        }
    }
}

解决办法

  • 检查代码中是否存在大量使用直接内存的情况,确保及时释放直接内存。
  • 可以通过 -XX:MaxDirectMemorySize 参数限制直接内存的大小。

总结

在面试中,要理解各种 OOM 情况的原因、表现和解决办法。当遇到 OOM 问题时,首先要通过日志和监控工具确定是哪种类型的 OOM,然后根据具体情况进行排查和解决。同时,要注意合理设置 JVM 参数,避免因内存配置不当导致 OOM。

posted @   皇问天  阅读(338)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示