王学岗性能优化————内存优化(一)(java虚拟机在运行时的数据区域、垃圾回收机制是如何确定内存回收的、java的四种引用、内存泄露)

第一:内存优化工具
内存优化的工具:DDMS MAT Finder-Activity,LeakCanary,LeakInspector等等。
第二:java虚拟机在运行时的数据区域
1,运行 时候的数据区域有两块东西,一个是线程私有区,每个线程都会开辟一块线程私有区;包括程序计数器,虚拟机栈,本地方法栈。另一个是共享数据区,所有的线程都可以访问,包括堆内存,包含常量池的方法区,
,2, 程序计数器是执行代码的指示器,用来确定下一行代码执行的位置。比如我们写一个方法,这个方法中有很多行的代码,程序计数器记录的就是当前执行代码的下一行代码的内存地址。当前行的代码执行完毕后,指导我们的程序执行下一行。如果有多个线程运行,每个线程都会有自己的程序计数器。在该区域是不存在out of memory的。
3,虚拟机栈,存放java方法,我们平时说的栈就是这块区域。该区域会出现oom和stackoverflow。局部变量、方法返回地址(方法调用结束后,返回地址会返回到栈中)都存储在该区域,该区域还有一个特点就是没有内存碎片(一段连续的存储空间,存储了图片,在使用过程中,有一张图片我们不需要了,这张图片在内存中被删除了。接下来我们又存储了一个信息,但是内存空间的占用要少于我们删除的图片,下一次存储比这块内存大的信息的时候会在这段连续的内存空间最后面存储,这样就会空出来一部分内存,这部分内存就叫做内存碎片,当我们一整块内存用的时间比较长的时候,这种内存碎块会大量存在)
4,本地方法栈会存放一些native方法。
5,堆内存容易发生内存泄露和内存溢出,这里面主要存储两类信息,对象的实例和数组的内容(数组的名字存到栈中)。这一块是虚拟机能管理的最大的一块内存,是GC的主战场。
6,常量池会存储一些字面量(使用public static final 修饰的东东)、常量,字符串(以char[]类型的数组存放)、类接口和方法的名字。
在这里插入图片描述
我们能优化的就是在堆内存。
第三:垃圾回收机制是如何确定内存回收的
GC算法:
1,引用计数算法和
当我们创建一个对象的时候,如Object o1=new Object();,这时候有一个变量会做+1操作, 即计数+1=1;这样堆去的内存就会有一个引用;接下来我们在写下面这段代码。
Object o2;
o2=o1;
这时候计数器又会+1 计数+1=2。现在有两个引用引用堆区。我们在写下面这段代码;
o1=null;
计数为1,即 计数-1=1;这时候会暴露一个问题,这个时候o1不会回收,因为此时O1的计数器为一,o2的计数器也为1;只有当计数=0的时候才会回收。这样我们就会造成了8个字节的内存无法回收。
我们在android中不适用这套算法;
2,可达性分析算法,这套算法是我们android使用的。
在这里插入图片描述

可作为GC ROOT的对象,虚拟机栈正在运行使用的引用,静态属性,常量,JNI引用的对象。
GC是需要2次扫描才回收对象,所以我们可以用finalize去救活丢失引用的对象
例如:

static App a;
@Override
protected void() throws Throwable{
	super.finalize();
	a=this;
}

第四,java的四种引用
虚引用PhantomReference:不对生存造成任何影响,用于跟踪GC的回收通知。
弱引用WeakReference:第一次扫到了,就标记下来,第二次扫到直接回收。也可以用于跟踪GC的回收通知

  @Test
    public void testWeakReference() throws InterruptedException {
        ReferenceQueue<Object> referenceQueuee=new ReferenceQueue<>();
        Object weakObject=new Object();
        //弱引用
        WeakReference weakReference=new WeakReference(weakObject,referenceQueuee);
        System.out.println("WeakReference:"+weakReference.get());//Object内存地址
        System.out.println("referenceQueuee:"+referenceQueuee.poll());//null

        weakObject=null;
        System.gc();
        Thread.sleep(2000);
        System.out.println("WeakReference:"+weakReference.get());//null
        System.out.println("referenceQueuee:"+referenceQueuee.poll());//object内存地址
    }



    @Test
    public void testPhantomReference() throws InterruptedException {
        //虚引用:功能,不会影响到对象的生命周期的,
        // 但是能让程序员知道该对象什么时候被 回收了
        ReferenceQueue<Object> referenceQueuee=new ReferenceQueue<>();
        Object phantomObject=new Object();
        PhantomReference phantomReference=new PhantomReference(phantomObject,referenceQueuee);
        phantomObject=null;
        System.out.println("phantomObject:"+phantomObject);//null
        System.out.println("phantomReference"+referenceQueuee.poll());//null
        System.gc();
        Thread.sleep(2000);
        System.out.println("referenceQueuee:"+referenceQueuee.poll());//地址
    }

软引用SoftReference:内存不足时回收,存放一些重要性不是很强又不能随便让清除的对象,比如图片切换到后台不需要马上显示了
第五:内存泄露
1,内存泄漏的原因:产生的原因:一个长生命周期的对象持有一个短生命周期对象的引用
通俗讲就是该回收的对象,因为引用问题没有被回收,最终会产生OOM
2,android profile的使用
选择run————profile app;就可打开这个工具
或者选择这个按钮。
在这里插入图片描述
我们运行以后就是这个画面
在这里插入图片描述
点击memory,选中一段内存。有四个字段Allocations Deallocations Total Count(堆中的实例数) ,shallow size(此堆中所有实例的总大小,以字节为单位).
最重要的就是shallow size,表示我们对象本身所占用内存的大小。
在这里插入图片描述

这里有一个转换格式工具D:\sdk\platform-tools目录下的hprof-conv.exe

3,优化一:经过查询,我们发现MainActivity切换到后台后仍有两个地方持有MainActivity引用,导致MainActivity无法回收。两个持有MainActivity引用的是mServedView和 mNextServedView。怎么办呢?我们可以在Activity中反射这两个成员,然后在onDestroy()中解除引用。

 @Override
    protected void onDestroy() {
        unRegisterBroadcastReceiver();
        super.onDestroy();
        method("mServedView");
        method("mNextServedView");
    }

    /**
     * 反射置空输入法中的属性
     * 传入的参数是一个属性,只要调用这个方法,就把这个属性置空。
     */
    public void method(String attr){
        //InputMethodManager:整个输入法框架(IMF)结构的核心API,应用程序之间进行调度和当前输入法交互。
        InputMethodManager im=(InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        //反射
        try{
            //拿到字段
            Field mCurRootViewField = InputMethodManager.class.getDeclaredField(attr);
            //不是public属性,设置权限
            mCurRootViewField.setAccessible(true);
            //取对象
            Object mCurRootView=mCurRootViewField.get(im);

            if (null!=mCurRootView) {
                Context context=((View)mCurRootView).getContext();
                if(context==this){
                    //破坏GC链
                    mCurRootViewField.set(im,null);
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }

猜你喜欢

转载自blog.csdn.net/qczg_wxg/article/details/89786992
今日推荐