【内存泄露】GC及WeakReference的使用

一、简单说一下GC回收:

GC :字面意思是垃圾回收器,让创建的对象不需要像c、c++那样delete、free掉 。对于c、c++的开发人员来说内存是开发人员分配的,也就是说还要对内存进行维护和释放。对于Java程序员来说,一个对象的内存分配是在虚拟机的自动内存分配机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,而且不容易出现内存泄露和内存溢出问题
GC:Garbage Collections 字面意思垃圾回收器,释放垃圾占用的空间。它与“java面向编程”一样是java语言的特性之一;它与“ c/c++语言”最大区别是不用手动调用 free() 和 delete() 释放内存。
GC 主要是处理 Java堆Heap ,也就是作用在 Java虚拟机 用于存放对象实例的内存区域,(Java堆又称为GC堆)。JVM能够完成内存分配和内存回收,虽然降低了开发难度,避免了像C/C++直接操作内存的危险。但也正因为太过于依赖JVM去完成内存管理,导致很多Java开发者不再关心内存分配,导致很多程序低效、耗内存问题。
在Java里, 当一个对象o被创建时, 它被放在Heap里. 当GC运行的时候, 如果发现没有任何引用指向o, o就会被回收以腾出内存空间. 或者换句话说, 一个对象被回收, 必须满足两个条件:
1、没有任何引用指向它 (对象存活性判断,常用的方法有两种:1.引用计数法; 2.对象可达性分析。
2、GC被运行.
其次我们要知道当我们调用System.gc()的时候,其实并不会马上进行垃圾回收,甚至不一定会执行垃圾回收。通过源码发现当我们直接调用System.gc()只会把这次gc请求记录下来,等到runFinalization=true的时候才会先去执行GC,runFinalization=true之后会在允许一次system.gc()。之后在call System.gc()还会重复上面的行为。
所以System.gc()要跟System.runFinalization()一起搭配使用才好。

System.gc()源码

/**
     * Indicates to the VM that it would be a good time to run the
     * garbage collector. Note that this is a hint only. There is no guarantee
     * that the garbage collector will actually be run.
     */
    public static void gc() {
        boolean shouldRunGC;
        synchronized(lock) {
            shouldRunGC = justRanFinalization;
            if (shouldRunGC) {
                justRanFinalization = false;
            } else {
                runGC = true;
            }
        }
        if (shouldRunGC) {
            Runtime.getRuntime().gc();
        }
    }

调用runFinalization()的时候justRanFinalization变为true,源码如下


/**
* Provides a hint to the VM that it would be useful to attempt
* to perform any outstanding object finalization.
*/
public static void runFinalization() {
        boolean shouldRunGC;
        synchronized(lock) {
            shouldRunGC = runGC;
            runGC = false;
        }
        if (shouldRunGC) {
            Runtime.getRuntime().gc();
        }
        Runtime.getRuntime().runFinalization();
        synchronized(lock) {
            justRanFinalization = true;
        }
}

由此可见,当我们需要调用的System.gc()的时候 要这样才会执行

System.gc();
runtime.runFinalizationSync();
System.gc();

不过个人建议不到万不得已不要调用,因为jvm有自己的gc策略,根本不需要我们来手动

二、内存泄露

导致内存泄漏例子:
A a = new A();
B b = new B(a);
a = null;
A对象的引用a置空了,a不再指向对象A的地址,我们都知道当一个对象不再被其他对象引用的时候,是会被GC回收的,很显然及时a=null,那么A对象也是不可能被回收的,因为B依然依赖与A,在这个时候,造成了内存泄漏!

三、WeakReference

WeakReference 弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
public class B
{
    WeakReference<A> weakA;
    public B(A a)
    {
        WeakReference<A> weakA = new WeakReference<A>(a);
    }
    public A getA()
    {
        return weakA.get();
    }
}
A a = new A();
B b = new B(a);
a = null;
b.getA();   // 返回null

注意的地方是 我们在调用时候。需要注意getA()为null的判断。

这样在我们平时项目中使用上下文以及事件传递handler的时候可以采用弱引用使用如下:

	//上下文
	public static WeakReference<DemoActivity> weak = null ;
	
	weak = new WeakReference<>(this);
	
	if (weak != null && weak.get() != null) {
		//初始化弹窗
	   PopupRoomManager manager = new PopupRoomManager(weak.get());
	}
	              

	//handler对象
    private Handler handler = new MyHandler(this);

    /**
     * 内部类:handler静态,弱引用封装
     */
    private static class MyHandler extends android.os.Handler {
        WeakReference<DemoActivity> weak;

        public UIHndler(DemoActivity activity) {
            weak = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            DemoActivity activity = weak.get();
            if (activity != null) {
                // 更新ui
                activity.handler(msg);
            }
        }
    }

    /**
     * 方法:handler ui刷新
     * @param msg 队列消息
     */
    private void handler(Message msg) {
        switch (msg.what) {
        case **:
					//*****
				break;
        }
    }

发布了49 篇原创文章 · 获赞 46 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/mingtiannihao0522/article/details/103143699