编者按:本文部分内容会有争议(下图评论),大家可以从中学到有用的内容,抱着学习的态度阅读和沟通,而不是批评的样子。很多人认为JAVA程序应该没有内存泄漏,因为有垃圾回收机制。事实上,如果我们在程序中不再使用某个对象,但是因为仍然有对它的引用,垃圾收集器是无法回收它的。对象占用的内存当然不能使用,这就造成了内存泄漏。如果我们的java运行时间长了,这种内存泄漏一直发生,***就会没有内存可用。当然,在java中,内存泄漏和C/C++是不一样的。如果java程序完全结束,它的所有对象都将无法访问,系统可以对它们进行垃圾回收。它的内存泄漏仅限于自身,不会影响整个系统。C/C++的内存泄漏更甚。它的内存泄漏是在系统级别。即使C/C++程序退出,其泄漏的内存也无法被系统回收,除非重启机器,否则永远无法使用。一个Android应用程序中的内存泄漏对其他应用程序几乎没有影响。为了让Android应用程序安全、快速地运行,每个Android应用程序都会使用一个专用的Dalvik虚拟机实例来运行,该实例由Zygote服务进程孵化,也就是说每个应用程序都运行在自己的进程中。Android为不同类型的进程分配不同的内存使用限制。如果程序在运行过程中发生内存泄漏,应用进程使用的内存超过了这个限制,就会被系统认为是内存泄漏而被杀死,这使得只有自己的进程被杀死,而不会影响其他进程(如果system_process等系统进程出现问题,会导致系统重启)。1.未释放引用导致的内存泄漏1.1未注册未注册导致的内存泄漏Android内存泄漏比纯Java内存泄漏更严重,因为其他一些Android程序可能会引用我们的Android程序对象(比如注册机制)。即使我们的Android程序已经结束,其他引用程序仍然有对我们Android程序对象的引用,泄漏的内存仍然无法被垃圾回收。例如例1:假设我们要监听系统中的电话服务,获取锁屏界面(LockScreen)中的一些信息(比如信号强度等),那么我们可以在LockScreen中定义一个PhoneStateListener对象,并注册它到服务中的TelephonyManager。对于LockScreen对象,当需要显示锁屏界面时会创建一个LockScreen对象,当锁屏界面消失时会释放LockScreen对象。但是如果我们在释放LockScreen对象时忘记取消之前注册的PhoneStateListener对象,就会导致LockScreen无法被垃圾回收。如果一直显示和消失锁屏界面,最终会导致OutOfMemory,因为大量的LockScreen对象无法回收,导致system_process进程挂掉。虽然有些系统程序貌似可以自动取消注册(当然不能及时),但是我们还是应该在我们的程序中明确取消注册,在程序结束的时候应该取消所有的注册。1.2集合中对象未清理导致的内存泄漏我们通常会在集合中添加一些对象引用。大的。如果集合是静态的,情况就更严重了。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中提出了一个方法:publicViewgetView(intposition,ViewconvertView,ViewGroupparent)为ListView提供每个item需要的视图对象。最初,ListView会根据当前屏幕布局从BaseAdapter实例化一定数量的视图对象,ListView会缓存这些视图对象。当ListView向上滚动时,顶部列表项的视图对象将被回收,然后用于构造新的底部列表项。这个构建过程是由getView()方法完成的。getView()的第二个形参ViewconvertView是缓存列表项的视图对象(如果初始化时缓存中没有视图对象,则convertView为null)。由此可见,如果我们不使用convertView,而是每次在getView()中重新实例化一个View对象,会浪费时间,造成内存垃圾,增加垃圾回收的压力。如果垃圾回收来不及,虚拟机将不得不分配更多的内存给应用进程,导致不必要的内存开销。ListView回收listitem视图对象的过程可以查看:viewplaincopytoclipboardprint?android.widget.AbsListView.java–>voidaddScrapView(Viewscrap)方法。在android开发中,主内存分配和垃圾回收必须时刻保持,因为系统分配给每个dalvik虚拟机的内存是有限的。在Google的G1中,分配的最大堆大小仅为16M。后来的机器一般都是24M,真是可怜。这就需要我们在开发过程中时刻注意。不要因为自己的代码问题导致OOM错误。JAVA内存管理:大家都知道android应用层是java开发的,androiddavlik虚拟机和jvm类似,只是它是基于寄存器的。因此,要了解android的内存管理,就必须了解java的内存分配和垃圾回收机制。在java中,new关键字用于为对象分配内存,释放内存由垃圾收集器(GC)回收。工程师在开发过程中不需要显式管理内存。但这可能会在不知不觉中浪费大量内存,最终导致java虚拟机在垃圾回收上耗费大量时间,更严重的是导致JVM的OOM。因此,java工程师了解JAVA的内存分配和垃圾回收机制还是很有必要的。存在的问题列举如下。点击detail进入,会列出可能有问题的详细代码:file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-32625.pngfile:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-21158.pngMAT的使用请参考:http://www.blogjava.net/rosen/...。html这位兄写的比较详细。总结不管是java还是android,都应该了解内存分配和垃圾回收机制。工程师很难写出没有糟糕代码的代码。关键是Android内存出现问题时如何排查。一、Android的内存机制Android程序是用Java语言编写的,所以Android的内存管理与Java的内存管理类似。程序员通过new为对象分配内存,所有对象都在java堆中分配空间;然而,对象的释放是由垃圾收集器完成的。C/C++中的内存机制是“谁污染,谁治理”。Java的比较人性化,我们请了专门的清洁工(GC)。那么GC如何确认一个对象是否已经被废弃了呢?Java使用有向图的原理。Java将引用关系视为图的有向边,有向边从引用者指向被引用对象。线程对象可以作为有向图的起始顶点。该图是从起始顶点开始的树。根顶点能到达的对象都是有效对象,GC不会回收这些对象。如果一个对象(连通子图)从根顶点不可达(注意该图是有向图),那么我们认为(这些)对象不再被引用,可以被GC回收。2.Android内存溢出Android内存溢出是怎么发生的?Android虚拟机是一个基于寄存器的Dalvik。它的最大heapsize一般是16M,有的机器是24M。因此,我们可以使用的内存空间是有限的。如果我们的内存使用超过一定程度,就会出现OutOfMemory错误。为什么内存不足?我认为主要有两个原因:由于我们程序的错误,我们长期保留对某些资源(如Context)的引用,导致内存泄漏,资源无法释放。保存多个消耗内存过多的对象(如Bitmap),导致内存超出限制。3.邪恶的staticstatic是Java中的关键字。当它用于修饰一个成员变量时,那么这个变量就属于类,而不是类的实例。所以用static修饰的变量的生命周期非常长。如果它被用来指代一些消耗过多资源的实例(Context是最常见的情况),那么就必须谨慎对待。sBackground是一个静态变量,但是我们发现我们并没有显式保存Context的引用。但是,当Drawable连接到View时,Drawable将View设置为回调。由于View包含了对Context的引用,所以,其实我们还是保存了Context的引用。引用链如下:Drawable->TextView->Context所以,最后还是没有释放Context,发生了内存泄露。怎样才能有效避免此类引用的发生呢?你应该尽量避免静态成员变量引用消耗过多资源的实例,例如Context。Context尽量使用ApplicationContext,因为ApplicationContext的生命周期比较长,在引用的时候不会出现内存泄露的问题。使用Wea??kReference而不是StrongReference。例如可以使用WeakReference
