如何定位和解决Android的tomcat堆内存溢出问题排查(大总

android 如何解决拍照大量图片时内存溢出,清理缓存问题
在相机拍照时有些图片要呈现缩略图,当多次拍照后,缓存不足容易崩
这种该怎么解决,或者有什么办法可以提前杀进程、清理缓存?
目前图片处理过按比例缩放,也用到了SoftReference
这些,就是当多次拍照后就出现闪退情况。预估计是缓存不足,内存溢出导致,各位高手有什么良策,还请多赐教,谢谢!!!
没有更多推荐了,android解决bitmap内存溢出之二
最近在做一款塔防游戏,用的事surfaceview框架,由于图片过多,而且游戏过程中都需要这些图片,所以加载成bitmap后造成OOM(out of memory)异常。下面是我一步一步找解决此问题的纪录,再此分享,希望对以后出现此问题的开发者有所帮助。
第一:出现问题,我的测试手机是2。2android操作系统,不会出现oom问题,但是在老板的android4.2上却出现了问题,因为是oom,所以我首先想到的是手动改变手机的内存大小限制。网上有些帖子说可以通过函数设置应用的HEAP SIZE来解决这个问题,其实是不对的。 VMRuntime.getRuntime().setMinimumHeapSize(NewSize); 堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。Max
Heap Size,是堆内存的上限值,Android的缺省值是16M(某些机型是24M),对于普通应用这是不能改的。函数setMinimumHeapSize其实只是改变了堆的下限值,它可以防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值时仍然采用堆的上限值,对于内存不足没什么作用。 setTargetHeapUtilization(float newTarget) 可以设定内存利用率的百分比,当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。在手机上进行了多次测试,确实不好使,在此,我断了改变内存限制的方法。
第二:查找出现问题的原因。1,在网上搜索bitmap内存溢出,找到很多说是因为图片大小引起的此问题。观察我的资源文件,没有太大的图片,只是图片数量过多,有将近900张,分别找出一张最大的图片和几张比较大的图片,单独测试,没有发现问题。方法1排除。
2,既然图片数量过多,突破点可能就是图片数量问题。于是分别找了200,400,600图片进行测试,在500左右的时候遇到错误,通过宝哥知道了将小图片整合存放到一张大图的方法,以此来减少图片的数量,但是仔细想想,加载成bitmap的时候还是要切割成小图生成bitmap,所以对此方法表示怀疑。由于以前没用过此方法,试试也无妨。所用到的工具是gdx—texturepackger,它只是一个工具,这里就不多说了。测试的最终结果是还是oom。方法2排除。
3,现在看来,既然不是图片数量的问题,而且会在500张左右的时候报错,那就可能是占用内存大小的问题了,Android手机有内存限制,但是我的图片大小又大于这个限制,这让我头疼了很长时间,研究国外的一些文章,从中发现了一些有用的信息,这些信息能够加深你对Android的解析bitmap机制的理解,在此分享:
As of Honeycomb Bitmap data is allocated in the VM heap.
作为蜂窝位图数据是在VM分配堆。)
There is a reference to it in the VM heap (which is small), but the actual data is allocated in the Native heap by the underlying Skia graphics library. 有一个引用在VM堆(小),但实际的数据是在本机堆分配由底层Skia图形库。
Unfortunately, while the definition of BitmapFactory.decode…() says that it returns null if the image data could not be decoded, the Skia implementation (or rather the JNI glue between the Java code and Skia) logs the message you’re seeing (“VM won’t let us
allocate xxxx bytes”) and then throws an OutOfMemory exception with the misleading message “bitmap size exceeds VM budget”. 不幸的是,虽然BitmapFactory.decode的定义…()表示,它返回null如果图像数据不能解码,Skia实现(或者说JNI胶之间的Java代码和Skia)日志消息你看到(“VM不会让我们分配xxxx字节”),然后抛出一个OutOfMemory异常与误导信息”位图的大小超过VM预算”。
The issue is not in the VM heap but is rather in the Native heap. 这个问题不是在VM堆而是在本机堆。
The Nat?ve heap is shared between running applications, so the amount of free space depends on what other applications are running and their bitmap usage. 本机堆是正在运行的应用程序之间共享,因此空闲空间的大小取决于其他运行程序,他们使用的位图。
However, I have found that getNativeHeapFreeSize() and getNativeHeapSize() are not reliable. 然而,我发现getNativeHeapFreeSize()和getNativeHeapSize()是不可靠的。
The Native heap size varies by platform. 本机堆大小不同的平台。
So at startup, we check the maximum allowed VM heap size to determine the maximum allowed Native heap size. 所以在启动时,我们检查最大允许VM堆大小来确定最大允许本机堆大小。
“Bitmap data is not allocated in the VM heap” — it is allocated on the VM heap as of Honeycomb “位图数据不是在VM分配堆”——这是VM分配的堆在蜂窝Yes. 是的。 As of Honeycomb (v3.0), bitmap data is allocated on the VM heap. 作为蜂窝(v3.0),位图数据堆上分配VM。 So all of the above only applies
to Gingerbread (v2.3.x) and before 所以所有上述只适用于姜饼(v2 3 x)和之前
这些信息零零散散,但是不难发现,问题的原因就在于根据Android版本的不同,bitmap data存放的位置是不同的,3.0以前是分配在native heap上,3.0以后是分配在VM heap上。
为了验证这个问题,我们需要抓去heap快照,众所周知,eclipse中的ddms可以查看heap信息,但是不够全面,这里我用到了adb shell dumpsys meminfo+包名 这条命令来查看heap信息,对比两个机子的不同如下:
从中不难发现,bitmap的存放位置根据Android版本的不同真的有所不同。好了,下面就是找出怎么把图片存放到native heap里就行了,BitmapFactory里就那么几个decode方法,很容易找到BitmapFactory .decodeStream就可以解决。下面贴一下代码:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.ARGB_8888;
options.inPurgeable =// 允许可清除
options.inInputShareable =// 以上options的两个属性必须联合使用才会有效果
String sname = String.format( “xxx.png”, sTowerStyle, j, sDirction, i);
InputStream is = am.open(sname);
arrBmp[ iBmpIndex] = BitmapFactory .decodeStream(is, null, options);
ok搞定手工。
小问题大发现:1.遇到问题,不要急躁。最初遇到这个问题的时候以为很好解决,试了几种方法后还是解决不了,内心难免会有挫败感,这个时候,最需要的是耐心。
2.网上有很多资源,但是能不能查得到就是自己的问题了,我发现那些编程老手们查找问题总是能够准确定位,快速的找到解决方法。以后要加强这方面的锻炼。
3.国内的资源大多偏向解决问题,国外的资源大多偏向分析问题,所以有的时候还是需要多看看外文的一些资料。当然这需要不错的英文功底。当初看外文的资料,头都大了。这是需要加强的一个方面。
没有更多推荐了,安卓开发中的内存溢出怎样解决_百度知道
安卓开发中的内存溢出怎样解决
我有更好的答案
一、内存溢出现在的智能手机内存已经足够大,但是对于一个应用程序来说智能手机当中稀缺的内存,仍然是应用程序的一大限制。在Android应用程序开发当中,最常见的内存溢出问题(OOM)是在加载图片时出现的,尤其是在不知道图片大小的情况下。潜在的内存溢出操作主要包括以下几点:1、从网络当中加载用户特定的图片。因为直到我们在下载图片的时候我们才知道图片的大小。2、向Gallery加载图片。因为现在智能手机的摄像头有很高的分辨率,在加载图片的时候需要最图片进行处理,然后才能正常的使用。请注意一点,Android系统是从系统全局的观念来分配内存以加载图片的,这就意味着,即使你的应用有足够大的内存可用,内存溢出问题(out of memroy,OOM)仍然可能出现,因为所有的应用共享一个加载图片的内存池(我们使用BitmapFactory进行解析)。二、解决内存溢出问题原文(Downsampling为了好理解,解释为,程序A)。程序A通过调整像素,同时使其均衡化来降低图片的分辨率。因为不管问题图片是因为太大而不能再手机上正常显现,这个图片都会缩短其宽度以在ImageView当中显示,当图片在ImageView当中显示时,我们会因为加载一些没有必要的原始图片而浪费掉内存。因此,更加有效的加载图片的时机是在其初始化处理的时候。以下是处理代码:1: private static Bitmap getResizedImage(String path, byte[] data, int targetWidth){2:3: BitmapFactory.Options options = new BitmapFactory.Options();14: options.inSampleSize =15:16: Bitmap bm =17: try{18: bm = decode(path, data, options);19: }catch(OutOfMemoryError e){39: result = result * 2;40:41: }42:43:44: }三、AQuery当在Android应用程序开发当中使用AQuery组件时,处理这个问题会变的更加的简单。
【0元入学,两周免费试听】
主营:培训【Python+人工智能,Java大数据,HTML5】
为您推荐:
其他类似问题
您可能关注的内容
内存溢出的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。持之以恒……!
Android内存(内存溢出 内存不足 内存低 .)优化详解.
Android内存(内存溢出 内存不足 内存低 .)优化详解
Android内存泄露
不少人认为JAVA程序,因为有垃圾回收机制,应该没有内存泄露。
其实如果我们一个程序中,已经不再使用某个对象,但是因为仍然有引用指向它,垃圾回收器就无法回收它,当然该对象占用的内存就无法被使用,这就造成了内存泄露。如果我们的java运行很久,而这种内存泄露不断的发生,最后就没内存可用了。当然java的,内存泄漏和C/C++是不一样的。如果java程序完全结束后,它所有的对象就都不可达了,系统就可以对他们进行垃圾回收,它的内存泄露仅仅限于它本身,而不会影响整个系统的。C/C++的内存泄露就比较糟糕了,它的内存泄露是系统级,即使该C/C++程序退出,它的泄露的内存也无法被系统回收,永远不可用了,除非重启机器。
Android的一个应用程序的内存泄露对别的应用程序影响不大。为了能够使得Android应用程序安全且快速的运行,Android的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行,它是由Zygote服务进程孵化出来的,也就是说每个应用程序都是在属于自己的进程中运行的。Android为不同类型的进程分配了不同的内存使用上限,如果程序在运行过程中出现了内存泄漏的而造成应用进程使用的内存超过了这个上限,则会被系统视为内存泄漏,从而被kill掉,这使得仅仅自己的进程被kill掉,而不会影响其他进程(如果是system_process等系统进程出问题的话,则会引起系统重启)。
1,引用没释放造成的内存泄露
1.1注册没取消造成的内存泄露
这种Android的内存泄露比纯java的内存泄露还要严重,因为其他一些Android程序可能引用我们的Anroid程序的对象(比如注册机制)。即使我们的Android程序已经结束了,但是别的引用程序仍然还有对我们的Android程序的某个对象的引用,泄露的内存依然不能被垃圾回收。
比如示例1:
假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。
但是如果在释放LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process进程挂掉。
虽然有些系统程序,它本身好像是可以自动取消注册的(当然不及时),但是我们还是应该在我们的程序中明确的取消注册,程序结束时应该把所有的注册都取消掉。
1.2集合中对象没清理造成的内存泄露
我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。
2,资源对象没关闭造成的内存泄露
资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。因为有些资源性对象,比如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。
程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。
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对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力,如果垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,造成不必要的内存开支。ListView回收list item的view对象的过程可以查看:
android.widget.AbsListView.java --& void addScrapView(View scrap) 方法。
示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view = new Xxx(...);
修正示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view =
if (convertView != null) {
view = convertV
populate(view, getItem(position));
view = new Xxx(...);
android.widget.AbsListView.java --& void addScrapView(View scrap) 方法。
示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view = new Xxx(...);
修正示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view =
if (convertView != null) {
view = convertV
populate(view, getItem(position));
view = new Xxx(...);
Android内存管理
在android的开发中,要时刻主要内存的分配和垃圾回收,因为系统为每一个dalvik虚拟机分配的内存是有限的,在google的G1中,分配的最大堆大小只有16M,后来的机器一般都为24M,实在是少的可怜。这样就需要我们在开发过程中要时刻注意。不要因为自己的代码问题而造成OOM错误。
JAVA的内存管理
大家都知道,android应用层是由java开发的,android的davlik虚拟机与jvm也类似,只不过它是基于寄存器的。因此要了解android的内存管理就必须得了解java的内存分配和垃圾回收机制。
在java中,是通过new关键字来为对象分配内存的,而内存的释放是由垃圾收集器(GC)来回收的,工程师在开发的过程中,不需要显式的去管理内存。但是这样有可能在不知不觉中就会浪费了很多内存,最终导致java虚拟机花费很多时间去进行垃圾回收,更严重的是造成JVM的OOM。因此,java工程师还是有必要了解JAVA的内存分配和垃圾回收机制。
上面这张图是JVM的结构图,它主要四个部分组成:ClassLoader子系统和执行引擎,运行时方法区和本地方法区,我们主要来看下RUNTIME DATA AREA区,也就是我们常说的JVM内存。从图中可以看出,RUNTIMEDATA AREA区主要由5个部分组成:
Method Area:被装载的class的元信息存储在Method Area中,它是线程共享的
Heap(堆):一个java虚拟机实例中只存在一个堆空间,存放一些对象信息,它是线程共享的
Java栈: java虚拟机直接对java栈进行两种操作,以帧为单位的压栈和出栈(非线程共享)
程序计数器(非线程共享)
本地方法栈(非线程共享)
JVM的垃圾回收(GC)
JVM的垃圾原理是这样的,它把对象分为年轻代(Young)、年老代(Tenured)、持久代(Perm),对不同生命周期的对象使用不同的垃圾回收算法。
年轻代(Young)
年轻代分为三个区,一个eden区,两个Survivor区。程序中生成的大部分新的对象都在Eden区中,当Eden区满时,还存活的对象将被复制到其中一个Survivor区,当此Survivor区的对象占用空间满了时,此区存活的对象又被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到年老代。
年老代(Tenured)
年老代存放的是上面年轻代复制过来的对象,也就是在年轻代中还存活的对象,并且区满了复制过来的。一般来说,年老代中的对象生命周期都比较长。
持久代(Perm)
用于存放静态的类和方法,持久代对垃圾回收没有显著的影响。
Android中内存泄露监测
在了解了JVM的内存管理后,我们再回过头来看看,在android中应该怎样来监测内存,从而看在应用中是否存在内存分配和垃圾回收问题而造成内存泄露情况。
在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中内存泄露定位
Mat(memory analyzer tools)是我们常用的用来定位内存泄露的工具,如果你使用ADT,并且安装了MAT的eclipse插件,你需要做的是进入DDMS视图的Devices视图:
点击"dump HPROF file"按钮,然后使用MAT分析下载下来的文件。
下面列出了存在的问题,点击detail进去,会列出详细的,可能会存在问题的代码:
关于MAT的使用可以参考:
这位兄弟写的比较详细。
不管是java还是android,都应该了解内存分配和垃圾回收机制,工程师要做到写的代码中没有bad code很难,关键是在出现问题的时候该怎么去排查
Android内存优化
一、 Android的内存机制
Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存,所有对象在java堆内分配空间;然而对象的释放是由垃圾回收器来完成的。C/C++中的内存机制是“谁污染,谁治理”,java的就比较人性化了,给我们请了一个专门的清洁工(GC)。
那么GC怎么能够确认某一个对象是不是已经被废弃了呢?Java采用了有向图的原理。Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象。线程对象可以作为有向图的起始顶点,该图就是从起始顶点开始的一棵树,根顶点可以到达的对象都是有效对象,GC不会回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。
二、Android的内存溢出
Android的内存溢出是如何发生的?
Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。
为什么会出现内存不够用的情况呢?我想原因主要有两个:
由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。
保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。
三、万恶的static
static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context的情况最多),这时就要谨慎对待了。
public class ClassName {
private static Context mC
以上的代码是很危险的,如果将Activity赋值到么mContext的话。那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。
我们举Android文档中的一个例子。
private static Drawable sB
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
label.setBackgroundDrawable(sBackground);
setContentView(label);
sBackground, 是一个静态的变量,但是我们发现,我们并没有显式的保存Contex的引用,但是,当Drawable与View连接之后,Drawable就将View设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。这个引用链如下:
Drawable-&TextView-&Context
所以,最终该Context也没有得到释放,发生了内存泄露。
如何才能有效的避免这种引用的发生呢?
第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。
第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。
第三、使用WeakReference代替强引用。比如可以使用WeakReference&Context& mContextR
该部分的详细内容也可以参考Android文档中Article部分。
四、都是线程惹的祸
线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new MyThread().start();
private class MyThread extends Thread{
public void run() {
super.run();
//do somthing
这段代码很平常也很简单,是我们经常使用的形式。我们思考一个问题:假设MyThread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。
由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时,MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。
有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重,Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。
这种线程导致的内存泄露问题应该如何解决呢?
第一、将线程的内部类,改为静态内部类。
第二、在线程内部采用弱引用保存Context引用。
解决的模型如下:
public abstract class WeakAsyncTask&Params, Progress, Result, WeakTarget& extends
AsyncTask&Params, Progress, Result& {
protected WeakReference&WeakTarget& mT
public WeakAsyncTask(WeakTarget target) {
mTarget = new WeakReference&WeakTarget&(target);
/** {@inheritDoc} */
protected final void onPreExecute() {
final WeakTarget target = mTarget.get();
if (target != null) {
this.onPreExecute(target);
/** {@inheritDoc} */
protected final Result doInBackground(Params... params) {
final WeakTarget target = mTarget.get();
if (target != null) {
return this.doInBackground(target, params);
/** {@inheritDoc} */
protected final void onPostExecute(Result result) {
final WeakTarget target = mTarget.get();
if (target != null) {
this.onPostExecute(target, result);
protected void onPreExecute(WeakTarget target) {
// No default action
protected abstract Result doInBackground(WeakTarget target, Params... params);
protected void onPostExecute(WeakTarget target, Result result) {
// No default action
事实上,线程的问题并不仅仅在于内存泄露,还会带来一些灾难性的问题。由于本文讨论的是内存问题,所以在此不做讨论。
由于51cto不让我一次传完,说我的字数太多了,所以分开传了。
五、超级大胖子Bitmap
可以说出现OutOfMemory问题的绝大多数人,都是因为Bitmap的问题。因为Bitmap占用的内存实在是太多了,它是一个“超级大胖子”,特别是分辨率大的图片,如果要显示多张那问题就更显著了。
如何解决Bitmap带给我们的内存问题?
第一、及时的销毁。
虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制。因此,在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。
第二、设置一定的采样率。
有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。如下面的代码:
private ImageV
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options);
preview.setImageBitmap(bitmap);
第三、巧妙的运用软引用(SoftRefrence)
有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。如下例:
/**本例子为博主随手一写,来说明用法,并未验证*/
private class MyAdapter extends BaseAdapter {
private ArrayList&SoftReference&Bitmap&& mBitmapRefs = new ArrayList&SoftReference&Bitmap&&();
private ArrayList&Value& mV
private Context mC
private LayoutInflater mI
MyAdapter(Context context, ArrayList&Value& values) {
mContext =
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
public int getCount() {
return mValues.size();
public Object getItem(int i) {
return mValues.get(i);
public long getItemId(int i) {
public View getView(int i, View view, ViewGroup viewGroup) {
View newView =
if(view != null) {
newView =(View)mInflater.inflate(R.layout.image_view, false);
Bitmap bitmap = BitmapFactory.decodeFile(mValues.get(i).fileName);
mBitmapRefs.add(new SoftReference&Bitmap&(bitmap));
//此处加入ArrayList
((ImageView)newView).setImageBitmap(bitmap);
return newV
六、行踪诡异的Cursor
Cursor是Android查询数据后得到的一个管理数据集合的类,正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉。
然而如果Cursor的数据量特表大,特别是如果里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android明显是倾向于编程者手动的将Cursor close掉,因为在源代码中我们发现,如果等到垃圾回收器来回收时,会给用户以错误提示。
所以我们使用Cursor的方式一般如下:
Cursor cursor =
cursor = mContext.getContentResolver().query(uri,null, null,null,null);
if(cursor != null) {
cursor.moveToFirst();
//do something
} catch (Exception e) {
e.printStackTrace();
10.} finally {
if (cursor != null) {
cursor.close();
有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭。
protected void onDestroy() {
if (mAdapter != null && mAdapter.getCurosr() != null) {
mAdapter.getCursor().close();
super.onDestroy();
CursorAdapter中的changeCursor函数,会将原来的Cursor释放掉,并替换为新的Cursor,所以你不用担心原来的Cursor没有被关闭。
你可能会想到使用Activity的managedQuery来生成Cursor,这样Cursor就会与Acitivity的生命周期一致了,多么完美的解决方法!然而事实上managedQuery也有很大的局限性。
managedQuery生成的Cursor必须确保不会被替换,因为可能很多程序事实上查询条件都是不确定的,因此我们经常会用新查询的Cursor来替换掉原先的Cursor。因此这种方法适用范围也是很小。
七、其它要说的。
其实,要减小内存的使用,其实还有很多方法和要求。比如不要使用整张整张的图,尽量使用9path图片。Adapter要使用convertView等等,好多细节都可以节省内存。这些都需要我们去挖掘,谁叫Android的内存不给力来着。
没有更多推荐了,

我要回帖

更多关于 内存溢出 问题 的文章

 

随机推荐