Android内存优化

Java内存分配

  • 方法区 ,虚拟机栈 本地方法栈
  • 堆 程序计数器
  • java内存回收算法
    • 标记 -清除算法,标记需要回收的对象,然后进行回收
      • 内存碎片化严重,内存不连续
    • 复制算法,将内存分成两块 ,内存整理后把对象复制到另一块内存,避免了内存碎片化严重的问题
      • 浪费了一块内存
    • 标记 -整理算法
      • 标记清除算法一样 ,但是整理后会把对方放在一块,避免了内存空洞,碎片化问题 ,也避免了复制算法的空间浪费
    • 分代收集算法
      • 新生代采用复制算法
      • 老年代采用标记整理算法,因为存活率高
Android 内存管理机制
  • 内存弹性分配,分配内存大小与厂商有关
  • OOM : 内存真正不足 ,可用内存不足
Davlik和Art区别
  • Dalvik回收算法固定
  • Art回收算法可运行期选择,Art具备内存整理能力,避免内存碎片化,内存空洞
Low Memory Killer
  • 进程分类 -可见进程 ,服务进程,后台进程,空进程

内存问题

  • 内存抖动 : 锯齿状 ,GC导致卡顿
    • 频繁创建对象,回收对象
  • 内存泄漏 : 可用内存减少,频繁GC
    • 内存中存在没有用的对象
    • 频繁泄漏,导致内存抖动
  • 内存溢出 : OOM.程序异常

工具使用

  • Memory Profiler
  • Memory Analyzer
  • LeakCanary
  • 查看应用内存使用情况 adb shell dumsys meminfo 包名或者pid
    内存信息
  • 使用adb shell procrank
    手机中的sh是经过精简过的,有些手机可能没有 procrank 命令,可以使用genymotion模拟器,或是自己安装procrank命令。使用procrank时,命令行的输出入下图:

https://blog.csdn.net/u011784994/article/details/78759137

可以看到,在linux下表示内存的耗用情况有四种不同的表现形式:
VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

VSS:VSS表示一个进程可访问的全部内存地址空间的大小。这个大小包括了进程已经申请但尚未使用的内存空间。在实际中很少用这种方式来表示进程占用内存的情况,用它来表示单个进程的内存使用情况是不准确的。
RSS:表示一个进程在RAM中实际使用的空间地址大小,包括了全部共享库占用的内存,这种表示进程占用内存的情况也是不准确的。
PSS:表示一个进程在RAM中实际使用的空间地址大小,它按比例包含了共享库占用的内存。假如有3个进程使用同一个共享库,那么每个进程的PSS就包括了1/3大小的共享库内存。这种方式表示进程的内存使用情况较准确,但当只有一个进程使用共享库时,其情况和RSS一模一样。
USS:表示一个进程本身占用的内存空间大小,不包含其它任何成分,这是表示进程内存大小的最好方式!
可以看到:VSS>=RSS>=PSS>=USS

  • .其它常用命令命令:
    adb shell kill PIDNumber 死你想杀死的后台进程来模拟某种 bug 的复现条件。
    adb shell ps 查看当前终端中的进程信息

那么如何在代码中判断当前的硬件系统有多少的 RAM 呢?在 Framework ProcessList.java 中有如下代码可用:
ProcessList() {
MemInfoReader minfo = new MemInfoReader();
minfo.readMemInfo();
mTotalMemMb = minfo.getTotalSize()/(1024*1024);
}

Memory Analyzer

  • 强大的java heap 分析工具,查找内存占用和泄漏
  • 生成整体的报告,分析内存问题
  • 线下深入使用
Memory Analyzer基本使用

实现方式

  • 分析待机内存 .重点模块内存 . OOM率
  • GC次数 GC时间

常规Dump Heap

  • Debug.dumpHprofData();

LeakCanary分析

  • build.gradle
dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-beta-3'
}

Bitmap内存模型

  • API10之前像素放在在Native层,回收不及时
  • API10之后放在堆内存
  • API26之后放在Native内存
  • getByteCount 获取bitmap占用内存
  • 一个像素占用内存大小(注意如果是放在资源文件还要乘以压缩比列)
ARTHook
  • 挂钩,将额外的代码钩住原来的方法,修改执行逻辑
    • 运行时插桩
    • 性能分析
  • Epic框架,Hook住一个java方法,使用范围4.0 - 9.0
    • api ‘me.weishu:epic:0.3.6’
    • 继承XC_MethodHook ,实现相应逻辑
    • 注入Hook:DexposedBridge.findAndHookMethod

线上监控

  • Debug.dumpHprofData();
  • 当app内存占比超过80% ,dump内存文件 ,空闲时上传 ,文件比较大,上传失败率高

打开 LargeHeap

  • 查看设备申请的内存大小
Runtime rt=Runtime.getRuntime();
long maxMemory=rt.maxMemory();
log.i("maxMemory:",Long.toString(maxMemory/(1024*1024)));
這個可以直接得到app可使用的最大memory size算出來是MB, 获得的是heapgrowthlimit
而dalvik.vm.heapsize是在manifest中设置了largeHeap=true 之后,可以使用的最大内存值
<application
     .....
     android:label="XXXXXXXXXX"
     android:largeHeap="true">
    .......
</application>

cat /system/build.prop  
 //读取这些值
dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=96m  //未使用  android:largeHeap="false"
dalvik.vm.heapsize=384m          //使用  android:largeHeap="true"
dalvik.vm.heaputilization=0.25
dalvik.vm.heapidealfree=8388608
dalvik.vm.heapconcurrentstart=2097152
ro.setupwizard.mode=OPTIONAL
ro.com.google.gmsversion=4.1_r6
net.bt.name=Android
dalvik.vm.stack-trace-file=/data/anr/traces.txt

谨慎使用SharedPreference

  • 会把数据全部加载到内存 ,数据大消耗内存多

GC 日志解读

  • 参考
    D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/
    9991K, external 4703K/5261K, paused 2ms 2ms

GC_XXX表明是哪类GC以及触发GC的原因。几种GC类型:

  • GC_CONCURRENT:这是因为你的heap内存占用开始往上涨了,为了避免heap内存满了而触发执行的。
  • GC_FOR_MALLOC:这是由于concurrent gc没有及时执行完而你的应用又需要分配更多的内存,内存要满了,这个时候不得不停下来进行malloc gc。
  • GC_EXTERNAL_ALLOC:这是为external分配的内存执行的GC,也就是上文提到的Bitmap Pixel Data之类的。
  • GC_HPROF_DUMP_HEAP:这是当你做HPROF这样一个操作去创建一个HPROF profile的时候执行的。
  • GC_EXPLICIT:这是由于你显式的调用了System.gc(),这是不提倡的,一般来说我们可以信任系统的GC。

freed 2049K表明在这次GC中回收了多少内存。
65% free 3571K/9991K是heap的一些统计数据,表明这次回收后65%的heap可用,存活的对象大小3571K,heap大小是9991K。
external 4703K/5261K是Native Memory的数据。放Bitmap Pixel Data或者是NIO Direct Buffer之类的。第一个数字表明Native Memory中已分配了多少内存,第二个值有点类似一个浮动的阀值,表明分配内存达到这个值系统就会触发一次GC进行内存回收。
paused 2ms 2ms表明GC暂停的时间。从这里你可以看到越大的heap size你需要暂停的时间越长。如果是concurrent gc你会看到2个时间一个开始一个结束,这时间是很短的,但如果是其他类型的GC,你很可能只会看到一个时间,而这个时间是相对比较长的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值