美文网首页
三、内存管理

三、内存管理

作者: Serenity那年 | 来源:发表于2018-10-28 21:26 被阅读3次
    一、java内存模型

    1.程序计数器pc-----线程私有

    占用内存很小;java的多线程是:抢占式的
    java中程序计数器pc为:虚拟机字节码地址;
    native中程序计数器为:undefined 也就是null;

    java虚拟机规范中没有定义此区域有oom

    2.虚拟机栈-----线程私有

    java虚拟机规范中定义了两种异常
    stackOverflow
    oom内存溢出
    包含:局部变量表,操作栈,方法返回地址,动态链接,额外附加信息;

    3.本地方法栈-----线程私有(Native Method Stack)
    和虚拟机栈差不多,一个是Java虚拟机的,一个是native层的

    java虚拟机规范中定义了两种异常
    stackOverflow
    oom内存溢出

    备注:在有些jvm的实现中,将本地方法栈和虚拟机栈合二为一了(代表:sun公司的hotpost虚拟机);


    4.java堆-----数据共享区
    虚拟机管理的最大一块内存。GC的主战场(垃圾堆),有oom异常;
    5.方法区-----数据共享区
    包含:常量,静态变量,类信息,即使编译后的java代码,特殊的class类,运行时常量池(字面量:java的常量,包括一些public static final,符号引用:类+接口 字段名 方法名 描述符)

    java内存模型.png
    二、引用类型

    强软弱虚
    强:Object object= new Object();
    软:有用,但不是必须的对象,在内存不足的时候,会将软引用回收;
    弱:非必须对象,gc扫过就会回收;
    虚:幽灵引用,不会对生存造成任何影响;没有办法得到引用的对象,但是在对象在回收的时候,能够得到通知;
    备注:
    1.避免OOM----->使用软引用
    2.内存得到及时的释放或回收;节省内存----->使用弱引用

    Object obj = new Object();
            //引用队列,当保存的对象被gc回收时,可以得到回收的引用对象
            ReferenceQueue<Object> objectReferenceQueue = new ReferenceQueue<>();
            SoftReference<Object> objectSoftReference = new SoftReference<>(obj,objectReferenceQueue);
    
            //得到保存的对象
            Object o = objectSoftReference.get();
            System.out.println("soft obj:"+o);
            obj = null ;
            //执行gc不能立即执行回收,只是通知gc进行扫描;
            System.gc();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            //当内存不足时,可以使用objectReferenceQueue.poll()得到引用的对象
            //内存不足时使用,可以得到;如果人为的将obj赋值为null,则得不到
            Reference<?> poll = objectReferenceQueue.poll();
            System.out.println("soft queue:"+poll);
            //soft obj:java.lang.Object@2df08e
            //soft queue:null;不为null代表被回收了
    

    ReferenceQueue不为null,代表被回收了;

    三、内存泄露

    内存泄露的根本原因:长生命周期的对象拥有短生命周期对象的引用,短生命周期对象无法被回收;也就是该被回收的对象因为引用问题无法被回收;

    1.Alloc Count :申请内存的次数,也就是申请的对象数;
    2.Shallow Size:对象占用的内存大小;
    3.Retained Size:对象引用组占用的大小;

    4.转换mat标准文件
    转换工具目录在/SDK/platform-tools/下面

    hprof-conv -z src dst :将src转换为dst
    例如:hprof-conv -z 1.hprof 1_mat.hprof

    5.系统输入法内存泄露bug:
    InputMethodManager ----内部类持有->mCurRootView---->也就是系统的DecorView---->这个DecorView持有mContext上下文对象;

    解决方法:

     @Override
        protected void onDestroy() {
            super.onDestroy();
    
            //去除系统级别的输入法造成的内存泄露
            /**
             * InputMethodManager ----内部类持有->mCurRootView---->也就是系统的DecorView---->这个DecorView持有mContext上下文对象;
             *
             * 这里可以把InputMethodManager看作是GCRoot,只有打断这条链,才能释放mCurRootView持有的上下文对象;
             * 因为无法直接获取InputMethodMananger中的mCurRootView对象,只有使用反射来获取
             */
            InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            Class<? extends InputMethodManager> aClass = inputMethodManager.getClass();
            try {
                //getDeclaredField() 获取全部的属性(不包含继承所得属性)     getField()只能获取public修饰的属性(包含继承所得)
                Field mCurRootViewField = aClass.getDeclaredField("mCurRootView");
                mCurRootViewField.setAccessible(true);
                Object mCurRootView = mCurRootViewField.get(inputMethodManager);
                if (null != mCurRootView) {
                    Context context = ((View) mCurRootView).getContext();
                    if (context == this) {
                        //破坏gc引用链
                        mCurRootViewField.set(inputMethodManager,null);
                    }
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
    
        }
    

    5.检查内存泄露 除了使用mat 还有使用LeakCanary工具;

    6.如何防止单例模式内存泄露?
    如果必须传入Activity的上下文对象,可以使用弱引用来持有,便于及时被gc回收;
    如果非必须 可以使用application的上下文对象;

    7.IO操作如何防止内存泄露?
    在IO操作中,必须要在finally中进行资源的关闭和释放;

    8.Handler或Thread引起的内存泄露?
    主要由于使用内部类的原因(非静态内部类持有外部类的引用);
    解决方式:使用静态内部类+弱引用方式;


    image.png

    创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息,


    image.png

    使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的Runnable和Message。

    Thread的内存泄露和Handler一样,处理方式也是一样的;Thread内存泄露的主要原因是因为线程生命周期的不可控。比如线程是 Activity 的内部类,则线程对象中保存了 Activity 的一个引用,当线程的 run 函数耗时较长没有结束时,线程对象是不会被销毁的,因此它所引用的老的 Activity 也不会被销毁,因此就出现了内存泄露的问题。

    9.集合类产生的内存泄露?
    集合类如果仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。如果这个集合类是全局性的变量 (比如类中的静态属性,全局性的 map 等即有静态引用或 final 一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减。

    10.静态成员引起的内存泄露?
    Static成员作为gc root,如果一个对象被static声明,这个对象会一直存活直到程序进程停止。

    相关文章

      网友评论

          本文标题:三、内存管理

          本文链接:https://www.haomeiwen.com/subject/jnptzftx.html