1. Android 内存大小
//获取的是正常情况下 app 内存大小,我的小米 5s 是 256 M
activiyManager.getMemoryClass()
//在Android manifest 中设置 android:largeHeap="true" 之后 app 内存大小
activiyManager.getLargeMemoryClass();//Unit : M
完整代码 :
private void calculate(){
StringBuilder stringBuilder = new StringBuilder();
ActivityManager activiyManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int memClass = activiyManager.getMemoryClass();// Unit : M
//Application : mainfest --> largeHeap --> not goog choice --> In most device, it has no difference. But in my MI 5S, it did work.
int LargememClass = activiyManager.getLargeMemoryClass();//Unit : M
stringBuilder.append("memClass : " + memClass + "\n");
stringBuilder.append("LargememClass : " + LargememClass + "\n");
info.setText(stringBuilder.toString());
Log.d("mooc", stringBuilder.toString());
在我的小米 5s 中, 数据 :
2. Android 内存管理方式
2.1 Android 系统内存分配和回收方式
Android 的底层是 Linux,开发应用的框架层是 Java,一个 App 通常就是一个进程对应一个虚拟机
体验一下:
- 记下你 app 的包名,我的 :
package com.lizi.nativeandroid;
- deploy app to your device
- windows 搜索栏 输入 cmd,找到Command Prompt,, 选择 Run as administartor
//进入 Android底层 Linux 系统命令,adb 需要配置环境变量
adb shell
//ps 查看系统里面进程的命令
在进程列表中根据包名找到自己 app 进程 :
它的 父进程 :
查看 app 进程相关信息
//dumpsys meminfo packagename
dumpsys meminfo com.lizi.nativeandroid
重新部署了下项目, id 变了,请忽略
操作系统把内存分配给进程之后, 进程在 app 中分配内存
- Dalvik Heap
- Native Heap
- apk
- …
GC 只有在 Heap 剩余空间不够时才发出垃圾回收, 如果内存不够,申请 GC 回收的时候 GC 里面有很多对象,很对变量,要知道 GC 的时候也会占用 处理器的时间的。这种极端情况下, GC 会影响你 app 的响应。
GC 触发时, 所有的线程都会被暂停
2.2App 内存限制机制
每个 App 非配的最大内存限制,随不同设备而不同
吃内存大户 : 图片
2.3 切换应用时后台 App 清理机制、
Android 系统通过 分时复用 使多个应用可以同时运行, 当你使用你的 app 时,点击 back or home , 并没有完全销毁,只是在后台的缓存中,下次运行的时候,直接从后台掉出来,所以速度非常快。
App 切换时的 LRU Cache
LRU 算法 : 最近使用的排在最前,最少使用的可能被清理掉
清理的时候 发出 onTrimMemory() 回掉方法,这并不只在清理时回掉, 系统内存有变化的时候会发出相应的 onTrimMemory() 给各个应用。
代码 :
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
Log.d("mooc", "level : "
2.4 监控内存的几种方法
2.4.1 代码监控
.getRuntime().totalMemory()*1.0f /(1024*1024);
Float freeMemory = Runtime.getRuntime().freeMemory()*1.0f /(1024*1024);
Float maxMemory = Runtime.getRuntime().maxMemory()*1.0f /(1024*1024);
stringBuilder.append("totalMemory : " + totalMemory + "\n");
stringBuilder.append("freeMemory : " + freeMemory + "\n");
stringBuilder.append("maxMemory : " + maxMemory + "\n");
25M 是系统分配的内存, 最大上限是 512M
2.4.2 Android Monitor | Android Profiler
2.4.3 Android Device Monitor
点击你的 app, 点击 Cause GC , 内存大小变化就会出来
3. App 内存优化方法
3.1 数据结构优化
3.1.1 频繁字符串拼接使用 StringBuilder
Talking is cheap, show me the code.
private int rowLength = 20;
private int length = 300;
private int [][] intMatrix = new int[rowLength][length];
private Random ran = new Random();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findView();
calculate();
for (int i = 0; i < rowLength; i++) {
for (int j = 0; j < length; j++) {
intMatrix[i][j] = ran.nextInt();
}
}
}
findViewById(R.id.starjoin).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("mooc", "starjoin clicked !");
doJoin();
}
});
findViewById(R.id.starbld).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("mooc", "starbld clicked !");
doBld();
}
});
private void doBld() {
StringBuilder rowStr = new StringBuilder();
Log.d("mooc", "doBld start : ");
for (int i = 0; i < rowLength; i++) {
for (int j = 0; j < length; j++) {
rowStr.append(intMatrix[i][j]);
rowStr.append(",");
}
Log.d("mooc", "doBld rowStr : " + i);
}
Log.d("mooc", "doBld rowStr : " + rowStr.toString().length());
}
private void doJoin() {
String rowStr = "";
Log.d("mooc", "doJoin start : ");
for (int i = 0; i < rowLength; i++) {
for (int j = 0; j < length; j++) {
rowStr = rowStr + intMatrix[i][j];
rowStr = rowStr + ",";
}
Log.d("mooc", "doJoin rowStr : " + i);
}
Log.d("mooc", "doJoin rowStr : "
当调用 doJoin(),性能 3S:
12-03 20:22:08.322 10917-10917/com.lizi.nativeandroid D/mooc: starjoin clicked !
12-03 20:22:08.322 10917-10917/com.lizi.nativeandroid D/mooc: doJoin start :
12-03 20:22:08.334 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 0
12-03 20:22:08.355 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 1
12-03 20:22:08.406 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 2
12-03 20:22:08.465 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 3
12-03 20:22:08.542 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 4
12-03 20:22:08.680 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 5
12-03 20:22:08.794 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 6
12-03 20:22:08.926 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 7
12-03 20:22:09.060 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 8
12-03 20:22:09.222 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 9
12-03 20:22:09.427 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 10
12-03 20:22:09.609 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 11
12-03 20:22:09.791 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 12
12-03 20:22:10.035 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 13
12-03 20:22:10.285 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 14
12-03 20:22:10.514 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 15
12-03 20:22:10.781 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 16
12-03 20:22:11.089 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 17
12-03 20:22:11.410 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 18
12-03 20:22:11.801 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 19
12-03 20:22:11.801 10917-10917/com.lizi.nativeandroid D/mooc: doJoin rowStr : 65928
12-03 20:22:11.802 10917-10917/com.lizi.nativeandroid I/Choreographer: Skipped 208 frames! The application may be doing too much work on its main thread.
12-03 20:22:26.867 10917-10917/com.lizi.nativeandroid D/mooc: level : 20
当调用 doBld(),性能 2 ms:
12-03 20:28:40.110 10917-10917/com.lizi.nativeandroid D/mooc: starbld clicked !
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld start :
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 0
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 1
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 2
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 3
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 4
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 5
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 6
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 7
12-03 20:28:40.111 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 8
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 9
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 10
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 11
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 12
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 13
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 14
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 15
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 16
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 17
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 18
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 19
12-03 20:28:40.112 10917-10917/com.lizi.nativeandroid D/mooc: doBld rowStr : 65928
3.1.2 ArrayMap 、SparseArray 替换 HashMap
- 内存使用更少
- 数据比较多的时候效率比较高
- 使用起来和 HashMap 用法一致
3.1.3 内存抖动
代码 :
private int rowLength2 = 10;
private int length2 = 420000;
private void doChurn(){
Log.d("mooc", "doChurn start : ");
for (int i = 0; i < rowLength2; i++) {
//重点,变量放在这里,每次重新一行赋值的时候,这里又有新的变量,上一行的变量就废弃,GC
//优化,变量放在脑袋上的循环外面重复使用就可以了
String[] strMatrix = new String[length2];
for (int j = 0; j < length2; j++) {
//内存抖动原因:大量字符串占用空间 --> 420000
strMatrix[j] = String.valueOf(ran.nextDouble());
}
Log.d("mooc", "doChurn strMatrix : " + i);
}
Log.d("mooc", "doChurn strMatrix end : "
3.1.4 再小的 Class 耗费 0.5 kb
3.1.5 HashMap 一个 entry 需要额外占用 32B
3.2 对象复用
- 复用系统自带的资源
- ListView/GridView 的 ConvertView 复用
- 避免在 onDraw 方法中执行对象的创建
3.3 避免内存泄漏
由于代码瑕疵,导致这块内存,虽然停止不用了,但是依然被其它东西引用这,使得 GC 没法对它回收
内存泄漏会导致剩余可用 Heap 越来越少,频繁触发 GC
4. OOM 问题优化
4.1 OOM 问题分析
Android 内存有限,展示图片容易OOM –> =-= | o.o | T_T
4.2 强引用、软引用的意义
private String strongRef;
private SoftReference softRef;
strongRef = String.valueOf(Math.random());
softRef = new SoftReference(String.valueOf(Math.random()));
场景 :
变量定义在 Activity 中,生命周期和 Activity 同步,Activity 存活期间这个变量所保存的内存空间不会被释放
强引用 : 在它的生命周期里它的内存空间不会被回收
软引用 : 在它的生命周期里它的内存空间 只要是内存不够, GC 可以把它回收掉
4.3 优化 OOM 问题的方法
- 注意临时 BItmap 对象的及时回收 -> 置空| .recyle()
- 注意 BItmap 的浪费
- Try catch 某些大内存非配的操作
- 加载 Bitmap :缩放比例、解码格式、局部加载 (解决绝大部分)
参考链接 : https://www.imooc.com/video/13672