内存优化

来源:互联网 发布:林州豫广网络客服电话 编辑:程序博客网 时间:2024/06/03 19:07

为什么进行内存优化

 - 为了防止APP运行内存限制,OOM导致APP崩溃 - APP性能:流畅性,响应速度和用户体验

那么我们可以用的内存为多少?

1.得到ActivityManager类的对象(系统类)

 ActivityManager activityManager= (ActivityManager)     getSystemService(Context.ACTIVITY_SERVICE);

2.调用这个类的方法查看可用内存

int Ram = activityManager.getMemoryClass();

3.当设置manifest文件中的application标签中largeHeap属性的值为”true” 时可以向系统索要将内存分配到最大化,这时可用内存大小如下(但是很多手机不支持这一功能,最后打印结果会和上面一样)

int largeRam = activityManager.getLargeMemoryClass();

上面两个都是以M为单位的
4.得到当前虚拟机运行的进程所占内存(以M的单位输出)

Float totalMemory = Runtime.getRuntime().totalMemory() * 1.0f / (1024 * 1024);

5.得到虚拟机空闲内存

Float freeMemory = Runtime.getRuntime().freeMemory() * 1.0f / (1024 * 1024);

6.得到虚拟机可以提供的最大内存

 Float maxMemory = Runtime.getRuntime().maxMemory() * 1.0f / (1024 * 1024);

很多人感到很疑惑,为什么,在java程序刚刚启动起来的时候freeMemory()这个方法返回的只有一两兆字节,而随着 java程序往前运行,创建了不少的对象,freeMemory()这个方法的返回有时候不但没有减少,反而会增加。
因为在java程序运行的过程的,内存总是慢慢的从操作系统那里挖的,基本上是用多少挖多少,但是java虚拟机100%的情况下是会稍微多挖一点的,这些挖过来而又没有用上的内存,实际上就是 freeMemory(),所以freeMemory()的值一般情况下都是很小的

运行结果,我是在模拟机上运行的
这里写图片描述

APP内存优化方法
1.数据结构的优化
2.对象的复用
3.避免内存泄漏

数据结构的优化
频繁的字符串拼接使用StringBuilder
字符串通过 + 的方式进行拼接,会产生中间字符串内存块,这些都是无用的,且耗时
代码演示:
我声明了一个二维数组,将二维数组进行拼接,每完成一次拼接我就会打印一次Log
看看哪个打印的Log用时最短,则拼接就是最快
首先是数组的声明

 private int rowLength=20;    private int length=100;    private int [] [] intMatrix=new int[rowLength][length];    private Random random=new Random();
for(int i=0;i<rowLength;i++){            for (int j=0;j<length;j++){                intMatrix[i][j]=random.nextInt();            }        }

String + 的方式拼接

private void doAdd() {        String str="";        for (int i=0;i<rowLength;i++){            for (int j=0;j<length;j++){                str=str+intMatrix[i][j];                Log.d("TAG","ADD");            }        }        Log.d("TAG","length:"+str.length());    }

这里写图片描述

这里写图片描述
所用时间 12s 左右
用StringBuilder拼接

StringBuilder sb=new StringBuilder();        for (int i=0;i<rowLength;i++){            for (int j=0;j<length;j++){                sb.append(intMatrix[i][j]);                Log.d("TAG","APP");            }        }        Log.d("TAG","length:"+sb.length());

这里写图片描述

这里写图片描述
所用时间1s不到

可以看出两者的差别了

数据结构优化还有

*ArrayMap,SparseArray替换HashMap*内存抖动

内存抖动是指在短时间内有大量的对象被创建或者被回收的现象,内存抖动出现原因主要是频繁(很重要)在循环里创建对象(导致大量对象在短时间内被创建,由于新对象是要占用内存空间的而且是频繁,如果一次或者两次在循环里创建对象对内存影响不大,不会造成严重内存抖动这样可以接受也不可避免,频繁的话就很内存抖动很严重),内存抖动的影响是如果抖动很频繁,会导致垃圾回收机制频繁运行(短时间内产生大量对象,需要大量内存,而且还是频繁抖动,就可能会需要回收内存以用于产生对象,垃圾回收机制就自然会频繁运行了)。综上就是频繁内存抖动会导致垃圾回收频繁运行。

先看运行效果
这里写图片描述
可以看到很明显的内存被大量占用然后又释放
代码如下

private void doChurn(){        //每一次都生成占用大量内存的数组对象        for(int i=0;i<num;i++){            String [] strMatrix=new String[length];            for(int j=0;j<length;j++){                strMatrix[j]=String.valueOf(random.nextDouble());            }            Log.d("TAG","churn:"+i);        }        Log.d("TAG","churn end");    }

因为我每次都在创建一个很庞大的数组对象,内存会上升当吃了一定内存时就会将我的内存回收,好有内存来创建新的对象,所以内存会下降,创建了一部分之后又会被回收

那么是怎么改进的呢

这里写图片描述
这样抖动是不是就减少了许多

private void optiChurn(){        String [] strMatrix=new String[length];        for(int i=0;i<num;i++){            for(int j=0;j<length;j++){                strMatrix[j]=String.valueOf(random.nextInt());            }            Log.d("TAG","opti churn:"+i);        }        Log.d("TAG","opti churn end");    }

在这个方法中将对象的创建都放到了循环外面,也就是说只创建一个对象,不断的赋值覆盖原来的值,所以就不会出现好多对象

当然还有一些会占用内存的东西是可以优化的,比如

*再小的Class耗费0.5kb*HashMap一个entry需要额外占用32B

对象的复用

*复用系统自带的资源--如果系统有了,就可以用系统的就不用在自己创建*ListView/GridView的ConvertView复用*避免在onDrawer中创建对象

避免内存泄漏
内存泄漏:由于代码瑕疵,导致这块 内存虽然是停止不用了,但是依然被其他东西引用着,导致GC没法对它回收
内存泄漏导致剩余可使用的Heap越来越少,频繁触发GC机制

那么怎么查看自己的内存是否存在泄漏呢
在这里推荐一个博文

http://www.cnblogs.com/yejiurui/p/3472765.html

如何才能知道我们的程序是否有内存泄漏的可能性呢。这里需要注意一个值:Heap视图中部有一个Type叫做data object,即数据对象,也就是我们的程序中大量存在的类类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。可以这样判断:
a) 不断的操作当前应用,同时注意观察data object的Total Size值;
b) 正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成很多对 象,而在虚拟机不断的进行GC的过程中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;
c) 反之如果代码中存在没有释放对象引用的情况,则data object的Total Size值在每次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大,
直到到达一个上限后导致进程被kill掉。
d) 此处已system_process进程为例,在我的测试环境中system_process进程所占用的内存的data object的Total Size正常情况下会稳定在2.2~2.8之间,而当其值超过3.55后进程就会被kill。

下面有个场景模拟
点击 A Activity中的按钮中开启一个耗时的线程,在点击返回到B在从B回到A,然后不断的切换,每次切换回A都会开启那个线程,从A到B本应该A界面被回收,但是因为一直在引用着A中的子线程,导致AB界面切换时A没办法进行回收,导致内存泄漏

这里写图片描述
很简单的操作,内存泄漏中代码

private class TextThread implements Runnable{        @Override        public void run() {            Log.d("TAG","LeakThread start");            while(true){                try {                    Thread.sleep(1000*60*5);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }

这里写图片描述

点击Cause GC进行垃圾回收后(GC回收后内存应该会变小,或者与一开始差距不大)

这里写图片描述

通过这些变大的数值可以看出GC并没有将这个东西进行回收,也就是产生了内存泄漏

当然改进的办法也很简单,缩小线程睡眠时间,在页面跳转的时候能保证A界面可以被回收

在声明一些全局变量时,用Application ,减少Activity Application不会被 回收,如果大量全局变量在Activity中使用会影响对Activity的回收
注意Cursor对象是否及时关闭

源码如下

https://github.com/XuDaHaoRen/RAMDemo

其实我还想整理OOM问题的,但是今天专业课老师讲了我一直都没弄懂的内容,要去整理那个啦~~~

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 中国公民在香港想去澳门怎么办 面包车不给贴膜怎么办 在香港海关被扣怎么办 发现被医院骗了怎么办 白皮子科染上血怎么办 宝宝头发里长湿疹怎么办 婴儿头发里长湿疹怎么办 宝宝解小便地方有湿疹怎么办 婴儿湿疹怎么办长在脸上 广州奥龙堡游泳卡过期了怎么办 大学生在学校当兵户口怎么办 茶叶梗枕头太硬怎么办 茶梗枕头太硬怎么办 照片放久了变红怎么办 乳腺萎缩和韧带松弛怎么办 航海王启航服务器爆满怎么办 LOL记分板没了怎么办 辅导孩子做作业没有耐心怎么办 宝宝住院三天回家不吃母乳怎么办 锁频君把应用变暗了怎么办 95的油加成92的怎么办 倒库一边宽了怎么办 倒库老是倒不好怎么办 倒库方向打早了怎么办 倒库左边小了怎么办 倒车入库小于30公分怎么办 倒库大于30公分怎么办 有行车记录仪遇到碰瓷怎么办 狗换了主人不吃怎么办 遇到扔东西碰瓷怎么办 碰见碰瓷的人怎么办 开店遇上碰瓷的顾客怎么办 我刮到别人的车怎么办 新车被刮了漆怎么办 停车擦到别人车怎么办 骑自行车被汽车撞了怎么办 车停在小区被刮怎么办 机动车被自行车撞了怎么办 单车撞小车后被起诉怎么办 给小车撞到电动单车怎么办 车停在路边被自行车撞怎么办