面试时的那些坑之内存泄漏和内存溢出

这也是我们面试时经常碰到的问题:
简单来说 内存溢出就是内存不够了。
内存泄漏就是有些对象我们不需要,但由于存在一些引用关系,GC无法回收。

要分析这两者,首先需要知道Android 系统是怎么去分配内存的。
我们需要确定这样几个概念

  1. Android中,内存的分配是以进程为最小单位。正常情况下,一个应用就是一个进程。
  2. 每一个进程得到的内存分为 堆,栈,和静态区。
  3. 堆存储创建的对象(new),栈存储对象的引用和基本类型(int ,float,double等)。
  4. 栈中存储的信息在存储之前就确定了存在时间的长度。(存在长度取决于它是全局变量,还是局部变量)
  5. 进程被分配的内存大小是可以变化的,但是有上限,到达上限进程就会被杀死。
  6. 我们常说的GC(Android 垃圾回收机制)只针对堆(即只是操纵堆中的对象)

我们再回过头说内存溢出的概念:
内存溢出,就是内存不够了。那为什么内存不够了,因为概念 5 ,我们被分配到的内存是有限的。怎么解决呢?
1、减少对象的内存占用
2、对象的重复使用
1.1 使用更加轻量级的数据结构,使用 Arraymap/SparseArray 来替换 HashMap;
1.2 避免在 Android 中使用枚举
1.3 减少Bitmap 的内存占用
1.3.1 合适的缩放比例
1.3.2 合适的编码格式
1.3.3 使用更小的图片
1.3.4 矢量图的使用
2.1 ListView 等的优化,例如使用 ViewHolder
2.2 对象池的使用
2.3 复用系统自带的资源
2.4 使用 inBitmap
2.5 避免在 onDraw() 方法中执行对象的创建
2.6 StringBuider 代替 + 的使用

内存泄漏呢,内存泄漏大多是因为我们写代码时不注意造成的。
我们每创建一个对象,这个对象就会被放到一个以 GCroot为根节点的树结构上。
垃圾回收的机制就是 回收那些存在于内存中,但根节点不可达的对象。
内存泄漏就是存在这样的一些对象,存在于内存中,根节点可达,但是我们不需要。
为什么会出现内存泄漏,我们应该怎么去避免它?
1、注意Activity 泄漏:
内部类引用导致Activity 的泄漏(Handler)
Activity 的 Context 传入其它类(SingleTask 单例模式)
没有及时回收临时的 Bitmap
监听器没有注销(Listener)
缓存容器中的对象泄漏
Cursor,receiver 等对象使用后没有关闭。
2、 内存使用策略优化
综合考虑设备内存的阈值和其它因素设计合适的内存大小
onLowMemory() 和 onTrimMemory() 的使用
谨慎使用 static 对象
Service 和 IntentService
谨慎使用抽象编程
谨慎使用第三方的库

面试的时候,也许会问到每一项中具体的内容,网上很多,在这儿就不赘述了。

那内存泄漏和内存溢出的发生会导致什么后果呢?
内存溢出(oom),进程被系统杀死。
内存泄漏,首先导致手机掉帧,卡顿,最后进程被系统杀死。
那我们继续想,为什么会出现卡顿呢?
不管是手机,还是电脑显示器,再或者gif 动画,视频。都是由一帧一帧(fps)(一张一张)的图片构成的。人眼看每秒 40–60fps 之间不觉的卡顿,小于40fps/s 就会感觉出卡顿。
Android 手机是每秒 60fps,大概 16毫秒一帧。
这 16毫秒内,cpu 要计算界面中显示的控件的大小,位置等,计算完毕之后交给 gpu 进行渲染,呈现到大家面前。那如果在 16 毫秒内无法完成,则会出现掉帧的现象,人会感觉到卡顿。
我们知道了卡顿的原理,继续说为什么会卡顿。因为内存泄漏,使得内存的使用量越来越大,这个时候会越来越频繁的触发 GC,而 GC 的操作是需要占用 cpu 的,也就会占用我们的 16 毫秒,也就会出现卡顿了。
那我们继续发问,什么时候才会触发 GC 呢?
有两种方式,一种是人为的进行调用,System.gc();人为的调用只会增加触发GC的概率,不一定会立即触发。
另外一种就是系统自己触发。
我们进程的堆空间,又被系统细分为 年轻态,老年态和持久态。新创建的对象放到年轻态,过一段时间之后如果没有被回收就放到老年态,以此类推,呆的越久,该对象因内存 不足而被回收的优先级就越低(就是越不容易被回收)。
当对象的总大小达到某一临界点的时候,系统就会触发GC。具体临界点是啥,众说风云,咱也不需要去深究这个东西,所以仅知道会触发GC 就可以了。

那我们遇到内存泄漏和内存溢出要怎么去找出泄漏的地方,怎么去解决呢?
建议大家看一下这个 Android性能优化的方方面面

<think>好的,我需要回答用户关于Java内存区域内存溢出异常的问题,特别是为了面试准备。首先,我要根据用户提供的引用资料来组织内容,确保涵盖各个内存区域,并解释对应的OutOfMemoryError情况及其处理方式。 首先,用户提到的引用[1]中提到了堆内存的OutOfMemoryError,包括GC Overhead Limit ExceededJava heap space错误。需要详细说明堆的作用以及这两种错误的原因处理方法。引用[2]提到了Java虚拟机栈本地方法栈,以及StackOverflowErrorOutOfMemoryError。需要区分这两个区域的用途可能出现的异常情况。引用[3][4]提供了JVM参数配置的建议,比如-Xms、-Xmx以及新生代的设置,这些在处理内存溢出很重要,需要包含在解决方案中。 接下来,我需要按照内存区域的划分来组织内容。通常Java内存区域包括方法区、堆、虚拟机栈、本地方法栈、程序计数器。其中,方法区堆是线程共享的,而虚拟机栈、本地方法栈程序计数器是线程私有的。 对于每个区域,要解释其作用、可能发生的内存溢出异常以及对应的处理方式。例如,堆溢出通常是由于对象过多且无法被回收,可以通过增加堆大小或优化代码来减少内存泄漏。方法区在JDK8之后是元空间,溢出可能与类加载过多有关,需要调整元空间大小。虚拟机栈本地方法栈的溢出通常与递归过深或线程数过多有关,需要调整栈的大小或优化代码结构。 在处理方式部分,要结合引用中的JVM参数,比如设置-Xmx调整堆大小,-XX:MaxMetaspaceSize调整元空间,-Xss调整栈大小等。同引用[4]中提到的调整新生代大小来减少Full GC次数,这也是优化堆内存的一部分。 还需要注意引用格式,每个引用在回答末尾要标注正确的脚注。例如,当提到堆内存溢出,引用[1][3]、[4]的相关内容需要正确标注。虚拟机栈部分引用[2]。 此外,用户要求生成相关问题,需要根据内容提出几个相关问题,如各个内存区域的参数配置、如何诊断内存泄漏、不同内存溢出异常的区别等。 最后,确保回答结构清晰,分点说明每个内存区域,然后处理方式,最后相关问题。使用中文,数学公式用$...$,独立公式用$$...$$,但当前内容可能较少涉及数学公式,但如果有的话需要注意格式。同代码块用正确格式,比如JVM参数示例可能需要用代码块展示。</think>### Java内存区域详解及内存溢出处理 #### 一、Java内存区域划分 1. **程序计数器** 线程私有,记录当前线程执行的字节码行号指示器。**唯一不会发生内存溢出的区域**。 2. **Java虚拟机栈** - **作用**:存储栈帧(局部变量表、操作数栈、动态链接、方法出口等)。 - **溢出场景**: - **StackOverflowError**:线程请求的栈深度超过限制(如无限递归)[^2]。 - **OutOfMemoryError**:栈容量动态扩展无法申请足够内存(如创建过多线程)[^2]。 - **参数**:`-Xss`调整栈大小(例如`-Xss256k`)。 3. **本地方法栈** 与虚拟机栈类似,但服务于Native方法。溢出表现与虚拟机栈相同[^2]。 4. **堆(Heap)** - **作用**:存放对象实例数组(线程共享)。 - **溢出场景**: - **`java.lang.OutOfMemoryError: Java heap space`**:对象数量超过堆容量(如内存泄漏)[^1]。 - **`GC Overhead Limit Exceeded`**:GC频繁且回收效率低(如大量短生命周期对象)[^1]。 - **参数**:`-Xms`(初始堆大小)、`-Xmx`(最大堆大小),建议设为物理内存的50%-60%[^3]。 5. **方法区(JDK8后为元空间)** - **作用**:存储类信息、常量、静态变量。 - **溢出场景**:`OutOfMemoryError: Metaspace`(如动态生成过多类)[^1]。 - **参数**:`-XX:MaxMetaspaceSize`调整元空间上限。 --- #### 二、内存溢出处理方式 1. **堆内存溢出** - **步骤**: 1. 使用`-XX:+HeapDumpOnOutOfMemoryError`生成堆转储快照。 2. 通过MAT或JProfiler分析对象占用,定位内存泄漏。 3. 调整`-Xmx`扩大堆空间,或优化代码(如避免循环引用)[^3][^4]。 2. **方法区溢出** - 减少动态类生成(如CGLib代理)。 - 设置`-XX:MaxMetaspaceSize=256m`限制元空间[^1]。 3. **栈溢出** - 优化递归算法(改为迭代)。 - 调整`-Xss`参数增大单线程栈容量[^2]。 4. **直接内存溢出** - 检查NIO的`ByteBuffer.allocateDirect()`使用,通过`-XX:MaxDirectMemorySize`限制直接内存。 --- #### 三、参数配置示例 ```shell # 堆内存配置 java -Xms2g -Xmx4g -XX:+UseG1GC \ -XX:MaxMetaspaceSize=256m \ -XX:MaxDirectMemorySize=512m \ -XX:+HeapDumpOnOutOfMemoryError \ -jar app.jar ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值