内存泄漏相关问题分析
作者:互联网
java内存泄漏基础知识
简单说就是该被释放的对象一直没有释放,一直被某个对象持有导致不能垃圾回收。
java内存分配策略
静态存储区
主要存放静态数据,全局变量等。
这块内存在程序编译的时候已经分配好了,并且静态存储区中存储的变量在程序运行期间一直存在
栈区
方法体内的局部变量会在栈区内创建内存空间,并在方法执行结束后,这些变量持有的内存会被自动释放
因为栈内存分配运算内置于处理器当中,所以效率很高
但是栈区的内存空间容量有限
堆区
堆区又称为动态内存分配
通常用来存放new出来的对象和数组
这部分内存在不使用的时候将由java的垃圾回收器进行回收
栈区和堆区的区别
栈区是方法体内定义的一些基本类型的变量和对象的引用变量,都是在方法的栈内存中分配的。
当在一段方法中定义一个变量时,java就会在栈中为该变量分配内存空间,当超过这个变量的作用域时,这个变量也就无效了,分配给它的内存空间也将被释放掉。这时候栈区分配的空间就可以被其他方法继续使用
堆区所存储的是所有new出来的对象和数组,在堆中分配的内存将由java的垃圾回收器来自动管理,其中产生的数组或者对象还可以在栈中定义一个特殊的变量,而这个变量就是在栈区所存储内存的地址。
java是如何管理内存的
比如图中,根定点可达的对象都是有效对象,GC的时候不会回收。
但是Object2这个对象,跟根定点之间没有引用链,是可以被垃圾回收的。
java中的内存泄漏
内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄漏
内存泄漏的不断积累,会造成OOM,内存溢出
android内存泄漏
单例
不恰当的使用单例会造成内存泄漏
单例的静态特性使得它的生命周期和app的生命周期是一样的
就是说如果一个对象不再需要使用,而单例对象一直持有这个对象的引用,那么这个对象将不能被正常释放,这就造成了内存泄漏。
比如代码中传入得conext是Activity的context,Activity在回收的时候由于被单例持有导致无法回收。
正确写法:
通过application的context代替Activity的context。单例的生命周期和application的生命周期是一样的。不会造成Activity回收不了。
匿名内部类
在Activity中有一个匿名内部类,TestResource,每次启动这个Activity时,都会使用整个类里面的一些数据。
这样虽然避免了资源的重复创建,但是这种写法会造成内存泄漏。
为什么?
因为java中非静态内部类默认会持有外部类的引用。
而又使用该内部类创建一个静态实例的话,比如TAG,该实例生命周期就和应用的一样长。就导致该静态实例一直持有该Activity的引用。
正确写法:
改成静态内部类,这样就不会持有外部类的引用,从而就不会有内存泄漏的问题。
Handler
这种Handler的写法就会造成内存泄漏,因为这种写法,创建的MHandler是Handler的非静态内部类的实例。持有外部类Activity的引用。
而Handler的消息队列是在looper中不断地轮询处理,当这个Activity要退出的时候,消息队列中还有未处理或者正在处理的消息,而消息队列中的Message又持有mHandler的实例引用,mhandler又持有了Activity的引用,导致Activity无法被回收。造成内存泄露
正确写法:
首先创建静态内部类MyHandler,然后持有的外部类Activity才用弱引用。
弱引用会在垃圾回收时立即回收,因此不会造成内存泄露。
尽量避免使用static变量
如果把成员变量声明为static,它的生命周期就和整个app的生命周期是一致的。这样就会导致一个问题,如果你的app进程,设计上是常驻内存的,那即使app切到后台,这部分static变量也是不会被释放的。
按照现有的内存管理机制,后台占用内存较大的应用将优先被回收。所以说当你的进程被回收了之后,你所存在的那些变量数据是不安全的。
在类设计的时候可以考虑懒加载,尽量避免static成员变量。
资源未关闭的内存泄漏
比如经常使用的广播接收者,bitmap,socket等,这些资源在Activity销毁时,需要关闭或者注销这些资源。
否则这些资源将不会被回收。
AsyncTask造成的内存泄漏
原因和Handler是一样的。
和Handler不同的是可以在Activity的onDestroy方法中执行AsyncTask的cancel方法,避免内存泄漏。
标签:分析,泄漏,java,变量,回收,内存,Activity 来源: https://www.cnblogs.com/cfdroid/p/16463610.html