三、内存管理

一、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声明,这个对象会一直存活直到程序进程停止。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • iPhone 4 以前 2007年,初代iPhone发布,屏幕的宽高是 320 x 480 像素。下文也是按照宽度...
    charlotte2018阅读 13,410评论 3 14
  • 儿子的游泳课因前段时间下雨,休息,今天才上完第七节课,但他还无法解开身上的浮板,另两位一起学的小朋友已经可以游玩自...
    rainbow33666阅读 1,621评论 1 2

友情链接更多精彩内容