避免OOM的一些实用的方法

本文详细分析了Android开发中常见的内存泄漏问题,包括static关键字滥用、Bitmap处理不当、背景图资源浪费、View缓存不当、对象生命周期管理错误、广播和服务绑定未解绑等方面,并提供了预防和解决措施。

对象都是有生命周期的,对象的生命周期有的是进程级别的,有的是Activity所在的生命周期,随Activity消亡;有的是Service所在的生命周期,随Service消亡。很多情况下判断对象是否合理存在的一个很重要的理由就是它实际的生命周期是否符合它本来的生命周期。很多Memory Leak的发生,很大程度上都是生命周期的错配,本来在随Activity销毁的对象变成了进程级别的对象,Memory Leak就无法避免了。


1、常见的MemoryLeak分析

1.1 频繁的使用static关键字修饰
很多初学者非常喜欢用static类static变量,声明赋值调用都简单方便。由于static声明变量的生命周期其实是和APP的生命周期一样的(进程级别)。大量的使用的话,就会占据内存空间不释放,积少成多也会造成内存的不断开销,直至挂掉。static的合理使用一般用来修饰基本数据类型或者轻量级对象,尽量避免修复集合或者大对象,常用作修饰全局配置项、工具类方法、内部类。

1.2 BitMap隐患
Bitmap的不当处理极可能造成OOM,绝大多数情况应用程序OOM都是因这个原因出现的。Bitamp位图是Android中当之无愧的胖子,所以在操作的时候必须小心。
1.2.1 及时释放recycle。由于Dalivk并不会主动的去回收,需要开发者在Bitmap不被使用的时候recycle掉。
1.2.2 设置一定的压缩率。需求允许的话,应该去对BItmap进行一定的缩放,通过BitmapFactory.Options的inSampleSize属性进行控制。如果仅仅只想获得Bitmap的属性,其实并不需要根据BItmap的像素去分配内存,只需在解析读取Bmp的时候使用BitmapFactory.Options的inJustDecodeBounds属性。
1.2.3最后建议大家在加载网络图片的时候,使用软引用或者弱引用并进行本地缓存,推荐使用android-universal-imageloader或者xUtils。

1.3 页面背景图
在布局和代码中设置背景和图片的时候,如果是纯色,尽量使用color;如果是规则图形,尽量使用shape画图;如果稍微复杂点,可以使用9patch图;如果不能使用9patch的情况下,针对几种主流分辨率的机型进行切图。

1.4 View缓存
在ListView和GridView中,列表中的很多项(convertView)是可以重用的,不需要每次getView就重新生成一项。另外,页面的绘制其实是很耗时的,findViewById也比较慢。所以不重用View,在有列表的时候就尤为显著了,经常会出现滑动很卡的现象。

1.5 引用地狱
Activity中生成的对象原则上是应该在Activity生命周期结束之后就释放的。Activity对象本身也是,所以应该尽量避免有appliction进程级别的对象来引用Activity级别的对象,如果有的话也应该在Activity结束的时候解引用。如不应用applicationContext在Activity中获取资源。
Service也一样。

1.6 BroadCastReceiver、Service 解绑
绑定广播和服务,一定要记得在不需要的时候给解绑。

1.7 handler 清理
在Activity的onDestroy方法中调用handler.removeCallbacksAndMessages(null);取消所有的消息的处理,包括待处理的消息;

1.8 Cursor及时关闭
在查询SQLite数据库时,会返回一个Cursor,当查询完毕后,及时关闭,这样就可以把查询的结果集及时给回收掉。

1.9 I/O流
I/O流操作完毕,读写结束,记得关闭。

1.10 线程
线程不再需要继续执行的时候要记得及时关闭,开启线程数量不易过多,一般和自己机器内核数一样最好,推荐开启线程的时候,使用线程池。

1.11 String/StringBuffer
当有较多的字符创需要拼接的时候,推荐使用StringBuffer。

 

2、总结

如果以上做的都比较完美了,可以肯定你的程序会运行的非常好。

当然,有的时候我们也会为了程序的效率性能把本来是Activity级里才用的资源提升到进程级别,比如ImageCache,或者其它DataManager等。

我只能说,空间和时间是相对的,有的时候需要牺牲时间换取空间,有的时候需要牺牲空间换取时间。内存是空间的存在,性能是时间的存在。完美的程序是在一定条件下的完美。

<think>嗯,用户想知道如何避免Java方法区发生OutOfMemoryError。首先,我得回忆一下方法区的作用和结构。方法区主要存储类信息、常量、静态变量等,Java 8之前是PermGen(永久代),之后变成了Metaspace(元空间)。所以,不同版本的解决方案可能不同。 首先,方法OOM通常是因为加载了过多的类,或者常量池太大,或者动态生成类过多。比如使用反射库如CGLIB或者大量JSP的情况。需要分析具体原因,可能用户的应用中有类似的情况。 根据引用[3],Java 8之后,PermGen被Metaspace取代,Metaspace使用本地内存,所以默认情况下可能更大,但也不是无限的。解决方案可能包括调整Metaspace的大小参数,比如-XX:MaxMetaspaceSize。如果用户没有设置这个参数,Metaspace可能会动态增长,但依然可能导致OOM,尤其是内存泄漏的情况下。 然后,内存泄漏的情况,比如长时间运行的应用程序,如果不断生成新的类而不卸载,就会导致Metaspace被占满。例如,动态代理框架频繁生成类,或者类加载器没有正确释放,导致类无法被垃圾回收。需要确保类加载器能被回收,从而卸载类。 另外,监控和诊断工具的使用也很重要。用户需要知道如何监控Metaspace的使用情况,比如用JVM参数打印GC日志,或者使用VisualVM、JConsole等工具。这样可以及时发现内存增长的趋势。 还有,减少不必要的类加载。比如避免重复加载类,或者使用第三方库时优化依赖,减少类数量。例如,使用JSP的应用如果生成过多Servlet类,可能需要预编译或者限制动态生成。 动态生成类的场景,比如ASM、CGLIB,需要注意控制生成类的数量,避免无限生成。或者使用缓存策略,复用已生成的类。 对于Java 8之前的PermGen,可能需要调整-XX:PermSize和-XX:MaxPermSize,但用户现在可能更多在用Java 8及以后,所以重点在Metaspace的配置。 总结一下,解决方案应该包括调整JVM参数,优化类加载,避免内存泄漏,监控和诊断,以及代码层面的优化。比如设置MaxMetaspaceSize,检查类加载器泄漏,减少动态类生成,使用工具监控等。 还需要考虑是否有第三方库导致的问题,如引用[4]提到的,第三方库可能有隐式内存问题。这时候需要分析堆转储,找出加载的类及其来源,确定是否有异常的类加载行为。 此外,引用[5]提到OOM和栈溢出的区别,但用户的问题明确是关于方法区的OOM,所以可以专注于方法区的特定原因和解决方案。 可能用户的应用在运行中出现了方法OOM,或者想提前预防。需要给出具体的步骤,比如如何设置JVM参数,如何检查类加载情况,推荐哪些工具等。同时,生成的相关问题可以涉及其他区域的OOM,或者如何诊断内存泄漏,或者不同Java版本的差异。</think>### 如何避免Java方法区发生OutOfMemoryError(OOM) Java方法区用于存储类元数据(如类名、方法信息、字段描述等)、常量池和静态变量。在Java 8之前,方法区通过**永久代(PermGen)**实现;Java 8及之后改为**元空间(Metaspace)**,使用本地内存管理[^3]。以下从原因分析、解决方案和优化实践三个层面展开: --- #### 一、方法OOM的常见原因 1. **类加载过多** - 动态生成大量类(如通过CGLIB、ASM等字节码操作库)。 - 频繁加载/卸载类(如长时间运行的Web应用中的JSP动态编译)。 2. **常量池过大** - 字符串常量池或符号引用过多(尤其是未合理复用常量)。 3. **元空间配置不当** - 未限制Metaspace大小,导致本地内存耗尽。 4. **类加载器泄漏** - 自定义类加载器未正确释放,导致类元数据无法回收。 --- #### 二、解决方案与优化实践 ##### 1. **调整JVM参数** - **Java 8+(Metaspace)** 设置元空间初始大小和上限: ```shell -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M ``` - **Java 7及之前(PermGen)** 调整永久代大小: ```shell -XX:PermSize=64M -XX:MaxPermSize=128M ``` ##### 2. **避免类加载泄漏** - **检查自定义类加载器**:确保类加载器的生命周期与加载的类一致,避免长期持有引用。 - **监控类卸载**:通过JVM参数`-XX:+TraceClassUnloading`观察类卸载情况。 ##### 3. **优化动态类生成** - **限制动态代理类数量**:例如在Spring框架中,避免滥用`@Scope("prototype")`。 - **复用生成的类**:缓存高频使用的动态类,减少重复生成。 ##### 4. **减少常量池压力** - **避免重复字符串常量**:使用`String.intern()`谨慎操作,防止常量池膨胀。 - **优化反射操作**:缓存`Class`对象和`Method`对象,避免频繁解析。 ##### 5. **监控与诊断工具** - **JVM内置工具**: - 通过`jstat -gcutil <pid>`查看Metaspace使用率。 - 添加JVM参数`-XX:+PrintGCDetails -XX:+PrintGCDateStamps`输出GC日志。 - **堆转储分析**: 使用`jmap -dump:format=b,file=heapdump.hprof <pid>`生成堆转储,通过MAT工具分析类加载器分布[^4]。 ##### 6. **第三方库与框架优化** - **检查类加载行为**:例如Tomcat的热部署可能导致PermGen OOM,可通过配置`reloadable="false"`减少类重新加载。 - **升级依赖库**:某些旧版本库(如CGLIB)可能存在内存问题,升级到修复版本。 --- #### 三、示例代码与配置 ```shell # 示例:启动时配置Metaspace限制 java -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=256M -jar app.jar ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值