log4j内存溢出解决方法都有哪些解决方法

Java常见内存溢出异常分析 - ImportNew
| 标签: ,
Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆内存分为了三部分,新生代,老年代,持久带,其中持久带实现了规范中规定的方法区,而内存模型中不同的部分都会出现相应的OOM错误,接下来我们就分开来讨论一下。
栈溢出(StackOverflowError)
栈溢出抛出java.lang.StackOverflowError错误,出现此种情况是因为方法运行的时候栈的深度超过了虚拟机容许的最大深度所致。
出现这种情况,一般情况下是程序错误所致的,比如写了一个死递归,就有可能造成此种情况。 下面我们通过一段代码来模拟一下此种情况的内存溢出。
import java.util.*;
import java.lang.*;
public class OOMTest{
public void stackOverFlowMethod(){
stackOverFlowMethod();
public static void main(String... args){
OOMTest oom = new OOMTest();
oom.stackOverFlowMethod();
运行上面的代码,会抛出如下的异常:
Exception in thread &main& java.lang.StackOverflowError
at OOMTest.stackOverFlowMethod(OOMTest.java:6)
堆溢出(OutOfMemoryError:java heap space)
堆内存溢出的时候,虚拟机会抛出java.lang.OutOfMemoryError:java heap space,出现此种情况的时候,我们需要根据内存溢出的时候产生的dump文件来具体分析(需要增加-XX:+HeapDumpOnOutOfMemoryErrorjvm启动参数)。出现此种问题的时候有可能是内存泄露,也有可能是内存溢出了。
如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。
如果出现了内存溢出问题,这往往是程序本生需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。
下面我们通过如下的代码来演示一下此种情况的溢出:
import java.util.*;
import java.lang.*;
public class OOMTest{
public static void main(String... args){
List&byte[]& buffer = new ArrayList&byte[]&();
buffer.add(new byte[10*]);
我们通过如下的命令运行上面的代码:
java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest
程序输入如下的信息:
[GC 1180K-&366K(19456K), 0.0037311 secs]
[Full GC 366K-&330K(19456K), 0.0098740 secs]
[Full GC 330K-&292K(19456K), 0.0090244 secs]
Exception in thread &main& java.lang.OutOfMemoryError: Java heap space
at OOMTest.main(OOMTest.java:7)
从运行结果可以看出,JVM进行了一次Minor gc和两次的Major gc,从Major gc的输出可以看出,gc以后old区使用率为134K,而字节数组为10M,加起来大于了old generation的空间,所以抛出了异常,如果调整-Xms21M,-Xmx21M,那么就不会触发gc操作也不会出现异常了。
通过上面的实验其实也从侧面验证了一个结论:当对象大于新生代剩余内存的时候,将直接放入老年代,当老年代剩余内存还是无法放下的时候,出发垃圾收集,收集后还是不能放下就会抛出内存溢出异常了
持久带溢出(OutOfMemoryError: PermGen space)
我们知道Hotspot jvm通过持久带实现了Java虚拟机规范中的方法区,而运行时的常量池就是保存在方法区中的,因此持久带溢出有可能是运行时常量池溢出,也有可能是方法区中保存的class对象没有被及时回收掉或者class信息占用的内存超过了我们配置。当持久带溢出的时候抛出java.lang.OutOfMemoryError: PermGen space。
我在工作可能在如下几种场景下出现此问题。
使用一些应用服务器的热部署的时候,我们就会遇到热部署几次以后发现内存溢出了,这种情况就是因为每次热部署的后,原来的class没有被卸载掉。
如果应用程序本身比较大,涉及的类库比较多,但是我们分配给持久带的内存(通过-XX:PermSize和-XX:MaxPermSize来设置)比较小的时候也可能出现此种问题。
一些第三方框架,比如spring,hibernate都通过字节码生成技术(比如CGLib)来实现一些增强的功能,这种情况可能需要更大的方法区来存储动态生成的Class文件。
我们知道Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回对常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。那么我们就可以通过String.intern方法来模拟一下运行时常量区的溢出.下面我们通过如下的代码来模拟此种情况:
import java.util.*;
import java.lang.*;
public class OOMTest{
public static void main(String... args){
List&String& list = new ArrayList&String&();
while(true){
list.add(UUID.randomUUID().toString().intern());
我们通过如下的命令运行上面代码:
java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest
运行后的输入如下图所示:
Exception in thread &main& java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at OOMTest.main(OOMTest.java:8)
通过上面的代码,我们成功模拟了运行时常量池溢出的情况,从输出中的PermGen space可以看出确实是持久带发生了溢出,这也验证了,我们前面说的Hotspot jvm通过持久带来实现方法区的说法。
OutOfMemoryError:unable to create native thread
最后我们在来看看java.lang.OutOfMemoryError:unable to create natvie thread这种错误。 出现这种情况的时候,一般是下面两种情况导致的:
程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过ulimit -u来查看此限制。
给虚拟机分配的内存过大,导致创建线程的时候需要的native内存太少。我们都知道操作系统对每个进程的内存是有限制的,我们启动Jvm,相当于启动了一个进程,假如我们一个进程占用了4G的内存,那么通过下面的公式计算出来的剩余内存就是建立线程栈的时候可以用的内存。 线程栈总可用内存=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存 通过上面的公式我们可以看出,-Xmx 和 MaxPermSize的值越大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。因此如果是因为这种情况导致的unable to create native thread,那么要么我们增大进程所占用的总内存,或者减少-Xmx或者-Xss来达到创建更多线程的目的。
因为String.valueOf(b)
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2016 ImportNew6631人阅读
性能优化(7)
一日凌晨,手机疯狂报警,短信以摧枯拉朽之势瞬间以百条的速度到达,我在睡梦中被惊醒,看到短信的部分内容如下:
Caused by: java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:597)
at java.util.Timer.&init&(Timer.java:154)
看到这个错误,我的第一感觉是创建了大量的线程,并且资源没有被回收,但是报错的却是其中一台应用服务器,表象看不太像是程序的问题,而此时在凌晨并发量也不应该会有这么大啊?同时我们不能因为报错暂停服务使用,而影响商户,所以决定要先解决问题,于是采用必杀技重启这台服务器,观察一小时内存溢出消失,问题暂时解决。
第二天白天这个问题并没有复现,我认为这是偶发事件,就没有过于在意,于是当晚再次出现内存溢出,并且还是随机某一台服务器爆出,我紧急找到监控部和系统部要求拿到栈信息内容和dump文件,然而并没有。。。。我们紧急开会做代码走查,发现这个应用其实是一个非常简单的应用,里面没有使用线程,也没有很复杂的逻辑,只是简单的增删改操作,那会是什么原因呢?
接下来我们的分析之路开始了。。。。。
一、现状情况
无法找到OOM时的javacore和heapdump文件。
无法还原问题发生时候系统内存被各个进程使用的占比,CPU的占比。
日志没有异常堆栈信息。
二、解决思路
1、要能够验证Tomcat配置内存溢出时打印堆栈并验证可行性,并保证在上线和重启不被擦除。
现状:当前只配置-XX:+HeapDumpOnOutOfMemoryError”,没有配置路径,不知道是被重启删除还是没有产生。
2、实现脚本,在出现OOM的时候,重启前,打印jstack栈信息和dump文件。
现状:目前是人工使用jmap和jstack打印CPU和内存信息给应急人员看。
3、实现JVM内存监控,在JVM内存紧张的时候提前报警,人工干预。
4.、监控或者实现脚本收集java进程OOM的时候,各个进程对内存的占比等,以及监控cache, buffer等。
现状:根据凌晨OOM的情况,错误堆栈表明,start0是JVM申请系统内存时内存不够,并非jvm堆内存不够而导致,需要上面信息查看详细的系统。
5、研究底层,寻找java.lang.OutOfMemoryError: unable to create new native thread错误的引发原因有哪些。
6、针对项目进行压测发现问题点。
三、深层次测试研究
**操作系统:**centos 7 64bit
**Linux内核:**Linux centos 3.10.0-327.10.1.el7.x86_64
**配置:**1G内存
**虚拟机工具:**virtual box 64bit
openjdk version “1.8.0_71”
OpenJDK Runtime Environment (build 1.8.0_71-b15)
OpenJDK 64-Bit Server VM (build 25.71-b15, mixed mode)
代码如下:
* PROJECT_NAME: test
* CREATE BY:
chao.cheng
public class OOMTest {
public static void main(String[] args) throws IOException {
if (args.length & 1) {
System.out.println("please input thread numbers!");
int threadNumber = Integer.parseInt(args[0]);
for (int i = 0; i & threadN i++) {
new Thread() {
public void run() {
while (true) {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}.start();
System.out.println("Thread " + i);
} catch (Throwable e) {
e.printStackTrace();
1、第一次测试过程:
输入命令如下:
显示:3584
注: ulimit -u是显示用户最多可开启的程序数目。
程序JVM参数设置如下:
java OOMTest 4000 -Xmx500m -Xss2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
运行结果如下:
查看系统内存如下:
结论分析:
a、在tmp目录下没有生成dump文件。
我们需要注意,使用-XX:+HeapDumpOnOutOfMemoryError参数的时候,并不一定在任何溢出场景下都会产生dump文件。
b、系统内存还有很多,却无法创建线程了。感觉是系统中存在的进程/线程已经达到系统配置的极限。
2、第二次测试过程:
使用ulimit -u 65535命令或者直接修改limits.conf文件,将max user process参数修改为65535。
jvm参数配置如下:
java OOMTest 4000 -Xmx500m -Xss2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
程序运行结果如下:
并没有报任何错误
修改jvm参数为:
java OOMTest 40000 -Xmx500m -Xss2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
配置只是修改了创建线程数为40000,比上一次4000多了10倍。
程序运行结果如下:
查看内存如下:
结论分析:
此时已经把系统的内存全部耗尽,无法使用free、top命令,此时已经无法执行任何命令
3、综合分析
a、当max user processers 设置的较小的时候,影响系统线程数目的是max user processers的设置
b、当max user processers设置为65535的时候,影响系统线程数目的是系统的内存。
c、对外的异常信息均为:OOM:unable to create native thread。
四、多线程内存溢出的理论支撑
通过上面的分析,我们看到其实多线程内存溢出有很大原因是因为系统设置和内存大小造成的,那么我们如何来分析当前系统配置能够支持多少线程呢?
对于java中的线程,我之前的理解一直是在java中new新线程的时候是直接使用jvm的内存,可实际情况却不是这样的。在java中每个线程需要分配线程内存,用来存储自身的线程变量,在jdk1.4中每个线程是256K的内存,在jdk1.5中每个线程是1M的内存,jdk1.6以上版本不太清楚。在java中每new一个线程,jvm都是向操作系统请求new一个本地线程,此时操作系统会使用剩余的内存空间来为线程分配内存,而不是使用jvm的内存。这样,当操作系统的可用内存越少,则jvm可用创建的新线程也就越少,我们举一个例子如下:
根据上面的知识,于是衍生出了下面这样的通用公式:
(MaxProcessMemory – JVMMemory – ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory:进程最大寻址空间。
JVMMMEMORY:jvm的内存空间(堆+永久区)-Xmx大小 (应该是实际分配大小)
ReservedOsMemory:操作系统预留内存
ThreadStackSize:-Xss大小
五、信息文件的导出
文章开始的时候说过,在内存溢出的时候,因为服务器重启导致jstack内容消失了,虽然配置了jvm参数HeapDumpOnOutOfMemoryError,但并没有产生相应的dump文件,于是我们采用脚本导出的方式,内容如下:
#!/bin/bash
ps -Leo pid,lwp,user,pcpu,pmem,cmd & /tmp/ps.log
pid=`ps aux|grep tomcat|grep xxx|awk -F ' ' '{print $2}'`
pstack $pid &/tmp/pstack.log
lsof & /tmp/sys-o-files.log
lsof -p $pid & /tmp/service-o-files.log
jstack -l $pid
& /tmp/js.log
free -m &/tmp/free.log
vmstat 2 1
在系统异常的时候,监控系统能够自动调用脚本产生信息文件,有了这些文件分析问题才能够得心应手,不然出了问题根本无从查起,只能是没头苍蝇乱撞。
六、压测分析
在压测环境中配置与生产环境一样的硬件环境和配置环境进行压测,可以看到如下的测试图:
通过压测分析,在程序并发线程达到1010个的时候,就报出unable to create new native thread异常,查看上面这张图其实不难看出,应用程序中并没有使用线程,但是在Log4j中却大量的使用了synchronized这个关键字,在并发非常高的时候会产生非常多的阻塞,最终内存资源耗尽报出内存溢出错误。
七、问题解决方案
问题解决方案
1、优化程序,减少没用的log4j日志输出,将log4j日志改为异步+buffer的模式。
2、单台服务器本身性能有限,通过增加服务器的方式提高扩展性。
3、将系统的一些限制属性增大,如:ulimit -a。
通用解决方案
上面的四点是针对我们的解决方案,但通过以上的这些分析,我们不难发现所有的unable to create new native thread的错误异常都有其共性的地方,那接下来我再总结一些相对通用一点的方法帮助大家在以后遇到类似的问题的时候,能够有据可查知道如何进行逐步的排查。
1、当发现这个错误的时候,第一时间要排查程序是否有bug,是否大量的创建了线程,或者没有正确使用线程池,比如:是否使用了Executors.newCachedThreadPool()方法,该方法能创建Integer最大值个线程,创建到一定程度的时候系统资源耗尽就会报错。
2、如果发现程序中并没有使用线程却依然报这个错,那么观察一下这个时刻的并发情况如何,要是溢出的这一时刻比其他时候并发量都要大,这时先查看一下系统资源的情况,使用ulimit –a查看max user processes和open files这二个属性的值越大,能创建的线程数也就越大。
3、如果以上二个属性调大依然报错的话,说明此时受限于系统内存资源了,要是服务器本身内存就比较小的话,建议增加内存。要是服务器内存比较大,就需要通过调整jvm参数来增加线程使用的内存,比如减小-Xss值,这个值越小能创建的线程数也就越多,也可以适当减少-Xmx和-Xms的值,增加堆外内存的容量。
八、回顾总结
在我们排查整个内存溢出问题的过程中,其实耗费了挺长时间,而且报错的时间基本都是在晚上,分析交易量看到这个时间段的并发量确实比白天要高,给我们最大的启示是发生问题的时候,不能很快的定位问题原因,没有最重要的报错日志可供分析。基于此我们开发了JAVA探针功能,可以实时采集当前服务器的内存使用情况、JVM堆使用情况,栈使用情况等等,并且能够提前预警,界面类似下面这样:
注:第一张图显示的内存使用的百分比,第二张图可以查看一段时间jvm内存使用情况,当高峰期来临时可以提前预警。
最后感谢在这次排查问题的过程中给与支持的晓辉同学。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:120256次
积分:1349
积分:1349
排名:千里之外
原创:178篇
评论:85条
阅读:15638
(1)(2)(7)(1)(4)(163)相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识。 在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识。jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。其中永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。堆区域用来存放Class的实例(即对象),对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。而Java栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。第一种OutOfMemoryError: PermGen
space 发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent
Generation space有关。解决这类问题有以下两种办法: 1.
增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh
或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行: JAVA_OPTS=" -XX:PermSize=64M
-XX:MaxPermSize=128m" 如果是windows服务器还可以在系统环境变量中设置。感觉用tomcat发布sprint+struts+hibernate架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署ssh项目的tomcat服务器经常宕机的问题。 2.
清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到tomcat共同的lib下,减少类的重复加载。这种方法是网上部分人推荐的,我没试过,但感觉减少不了太大的空间,最靠谱的还是第一种方法。第二种OutOfMemoryError:&
space 发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap
space有关。解决这类问题有两种思路: 1.
检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。 我以前写一个使用K-Means文本聚类算法对几万条文本记录(每条记录的特征向量大约10来个)进行文本聚类时,由于程序细节上有问题,就导致了Java
heap space的内存溢出问题,后来通过修改程序得到了解决。 2.
增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m
-Xmx1024m第三种OutOfMemoryError:unable to create new native
thread 这种错误在Java线程个数很多的情况下容易发生
阅读(...) 评论()如何定位和解决Andorid的内存溢出问题(大总结)
我们经常在做项目过程中遇到内存溢出的问题,同时面试中关于OOM的问题也常常出现。
这里,我将前辈们解决Andorid内存溢出的方法重新整理一番,方便自己以后使用。最后附上参考博文。
一、Android的内存机制
android应用层是由java开发的,android的davlik虚拟机与jvm也类似,只不过它是基于寄存器的。在java中,通过new为对象分配内存,所有对象在java堆内分配空间;而内存的释放是由垃圾收集器(GC)来回收的。 Java采用了有向图的原理。Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象。线程对象可以作为有向图的起始顶点,该图就是从起始顶点(GC roots)开始的一棵树,根顶点可以到达的对象都是有效对象,GC不会回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。
二、Android的内存溢出原因
1、内存泄露导致
由于我们程序的失误,长期保持某些资源(如Context)的引用,垃圾回收器就无法回收它,当然该对象占用的内存就无法被使用,这就造成内存泄露。
中常见就是Activity被引用在调用finish之后却没有释放,第二次打开activity又重新创建,这样的内存泄露不断的发生,则会导致内存的溢出。
Android的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行,它是由Zygote服务进程孵化出来的,也就是说每个应用程序都是在属于自己的进程中运行的。Android为不同类型的进程分配了不同的内存使用上限,如果程序在运行过程中出现了内存泄漏的而造成应用进程使用的内存超过了这个上限,则会被系统视为内存泄漏,从而被kill掉,这使得仅仅自己的进程被kill掉,而不会影响其他进程. <span style="color:#、占用内存较多的对象
保存了多个耗用内存过大的对象(如Bitmap)或加载单个超大的图片,造成内存超出限制。
三、常见的内存泄漏问题及其解决方案
1、引用没释放造成的内存泄露
1.1注册没取消造成的内存泄露
&这种Android的内存泄露比纯Java的内存泄漏还要严重,因为其他一些Android程序可能引用系统的Android程序的对象(比如注册机制)。即使Android程序已经结束了,但是别的应用程序仍然还有对Android程序的某个对象的引用,泄漏的内存依然不能被垃圾回收。
1.2集合中对象没清理造成的内存泄露
我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。
1.3 static
&static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。
&&& private static ActivitymC&&&&&& //省略
如何才能有效的避免这种引用的发生呢?
&&& 第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。
&&& 第二、Context尽量使用ApplicationContext,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。
看使用的周期是否在activity周期内,如果超出,必须用application;常见的情景包括:AsyncTask,Thread,第三方库初始化等等。
还有些情景,只能用activity:比如,对话框,各种View,需要startActivity的等。
总之,尽可能使用Application。
&&& 第三、使用WeakReference代替强引用。比如可以使用WeakReference&Context&mContextR
1.4、线程(内部类的使用)
线程产生内存泄露的主要原因在于线程生命周期的不可控。如果我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时,MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。
如果非静态内部类的方法中,有生命周期大于其所在类的,那就有问题了。比如:AsyncTask、Handler,这两个类都是方便开发者执行异步任务的,但是,这两个都跳出了Activity/Fragment的生命周期。
&&& 第一、将线程的内部类,改为静态内部类。
&&&&&& 原因:
因为非静态内部类会自动持有一个所属类的实例,如果所属类的实例已经结束生命周期,但内部类的方法仍在执行,就会hold其主体(引用)。也就使主体不能被释放,亦即内存泄露。静态类编译后和非内部类是一样的,有自己独立的类名。不会悄悄引用所属类的实例,所以就不容易泄露。
&&& 第二、如果需要引用Acitivity,使用弱引用。
2、资源对象没关闭造成的内存泄露
资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。而不是等待GC来处理。它们的缓冲不仅存在于java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。因为有些资源性对象,比如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。而且android数据库中对Cursor资源的是又限制个数的,如果不及时close掉,会导致别的地方无法获得。
3、一些不良代码成内存压力
有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存,对内存的回收和分配造成很大影响的,容易迫使虚拟机不得不给该应用进程分配更多的内存,造成不必要的内存开支。
3.1 Bitmap没调用recycle()
Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才设置为null.
虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试结果显示它并没能立即释放内存。但是我猜应该还是能大大的加速Bitmap的主要内存的释放。
3.2 构造Adapter时,没有使用缓存的&convertView
&以构造ListView的BaseAdapter为例,在BaseAdapter中提共了方法:
public&View&getView(int&position,&View&convertView,&ViewGroup&parent)
来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list&item的view对象会被回收,然后被用来构造新出现的最下面的list&item。这个构造过程就是由getView()方法完成的,getView()的第二个形参&View&convertView就是被缓存起来的list&item的view对象(初始化时缓存中没有view对象则convertView是null)。
&&&&由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力,如果垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,造成不必要的内存开支。
四、占用内存较多的对象(图片过大)造成内存溢出及其解决方案
因为Bitmap占用的内存实在是太多了,特别是分辨率大的图片,如果要显示多张那问题就更显著了。Android分配给Bitmap的大小只有8M。
方法1:等比例缩小图片
有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一
尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。
因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source, decodeStream最大的秘密在于其直接调用JNI&&nativeDecodeAsset()来完成decode, 无需再使用java层的createBitmap,从而节省了java层的空间。
方法2:对图片采用软引用,及时地进行recycle()操作
虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制。因此,在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。
SoftReference&Bitmap&
bitmap = new SoftReference&Bitmap&(pBitmap);
if(bitmap != null){
if(bitmap.get() != null && !bitmap.get().isRecycled()){
bitmap.get().recycle();
方法3:&单个页面,横竖屏切换N次后 OOM
<span style="color:#.&看看页面布局当中有没有大的图片,比如背景图之类的。去除xml中相关设置,改在程序中设置背景图(放在onCreate()方法中):
Drawable bg = getResources().getDrawable(R.drawable.bg);
XXX.setBackgroundDrawable(rlAdDetailone_bg);
在Activity destory时注意,bg.setCallback(null);&防止Activity得不到及时的释放。
<span style="color:#.&跟上面方法相似,直接把xml配置文件加载成view&再放到一个容器里,然后直接调用 this.setContentView(View view);避免xml的重复加载。
方法4:在页面切换时尽可能少地重复使用一些代码。比如:重复调用数据库,反复使用某些对象等等.....
方法5:Android堆内存也可以自己定义大小和优化Dalvik虚拟机的内存
--http://blog.csdn.net/wenhaiyan/article/details/5519567
&&&&注意:若使用这种方法:project build target&只能选择 &= 2.2 版本,否则编译将通不过。所以不建议用这种方式。
五、Android中内存泄露监测
内存监测工具 DDMS --& Heap
使用方法比较简单:
&&&&&&&&&选择DDMS视图,并打开Devices视图和Heap视图
&&&&&&&&&点击选择要监控的进程,比如:上图中我选择的是system_process
&&&&&&&&&选中Devices视图界面上的&update heap&&图标
&&&&&&&&&点击Heap视图中的&Cause GC&&按钮(相当于向虚拟机发送了一次GC请求的操作)
在Heap视图中选择想要监控的Type,一般我们会观察dataobject的&total size的变化,正常情况下total size的值会稳定在一个有限的范围内,也就说程序中的代码良好,没有造成程序中的对象不被回收的情况。如果代码中存在没有释放对象引用的情况,那么data object的total size在每次GC之后都不会有明显的回落,随着操作次数的增加而total size也在不断的增加。(说明:选择好data object后,不断的操作应用,这样才可以看出total size的变化)。如果totalsize确实是在不断增加而没有回落,说明程序中有没有被释放的资源引用。那么我们应该怎么来定位呢?
Android中内存泄露定位
通过DDMS工具可以判断应用程序中是否存在内存泄漏的问题,那又如何定位到具体出现问题的代码片段,最终找到问题所在呢?内存分析工具MAT Memory Analyzer Tool解决了这一难题。MAT工具是一个Eclipse 插件,同时也有单独的RCP 客户端,MAT工具的解析文件是.hprof,这个文件存放了某进程的内存快照。MAT工具定位内存泄漏具体位置的方法如下:
&&&① 生成.hprof文件。Eclipse中生成.hprof文件的方法有很多,不同Android版本中生成.hprof的方式也稍有差别,但它们整体思路是一样的。我们在DDMS界面选中想要分析的应用进程,在Devices视图界面上方的一行图标按钮中,同时选中“Update Heap”和“Dump HPROF file”两个按钮,这时DDMS将会自动生成当前选中进程的.hprof文件。
&&&② 将.hprof 文件导入到MAT工具中,MAT工具会自动解析并生成报告,点击“Dominator Tree”按钮,并按包分组,选择已定义的包类点右键,在弹出的菜单中选择List objects?﹥With incoming references,这时会列出所有可疑的类。右键点击某一项,并选择Path to GC Roots?﹥excludeweak/soft references,MAT工具会进一步筛选出跟程序相关的所有内存泄漏的类。这样就可以追踪到某一个产生内存泄漏的类的具体代码中。
&&&使用MAT内存分析工具查找内存泄漏的根本思路是找到哪个类的对象的引用没有被释放,然后分析没有被释放的原因,最终定位到代码中哪些片段存在着内存泄漏。
参考博文:
http://blog.csdn.net/ronaldong99/article/details/8227818
http://articles.e-/embedded/article103890.htm
http://jackxlee./450
http://smallwoniu./8875&
正要总结的,辛苦了~
本分类共有文章18篇,更多信息详见
& 2012 - 2016 &
&All Rights Reserved. &
/*爱悠闲图+*/
var cpro_id = "u1888441";

我要回帖

更多关于 内存溢出解决方法 的文章

 

随机推荐