App架构师实践指南六之性能优化三

2018年08月02日 13:57:57 nicolelili1 阅读数:190
 

内存性能优化
1、内存机制和原理

1.1 内存管理
内存时一个基础又高深的话题,从认识内存到使用内存,再到管理内存,伴随着编程生涯。
程序本身只是一个内存中数据不断迁移和CPU不断进行数值运算的过程,一层层高级语言和软件工程将这个复杂过程更加条理有序地去组织了,避免了“重复制造车轮”的繁琐,但内存问题的本身是不可避免的。

1.2 Android内存机制
Android本身既支持java,又支持C/C++,框架上又基于Linux上承接Android Framework。

1.2.1 java内存机制

---java内存区域。java内存区域可划分为方法区、堆、栈以及程序计数器。
------方法区(Method Area)。默认最大容量64MB,存放类的结构(方法和属性)、静态成员等,运行时的常量池,被所有线程共享的内存区域,属于持久代。
------堆(Heap)。默认最大容量是64MB,存放对象持有的数据,同时保持对原类的引用,被所有线程共享的内存区域。
------栈(Stack)。分虚拟机栈(JVM Stacks)和本地方法栈(Native Method Stacks),前者用于存储局部变量表、动态连接、操作数、方法出口等信息,有两种可能的java异常---StackOverFlowError和OutOfMemoryError,为java方法服务;而后者为Native方法服务。默认最大容量为1MB,方法调用结束后,java虚拟机会回收栈占用的内存,线程私有内存区域。
------程序计数器(Program Counter Register)。可以看作是当前线程执行字节码的行号指示器,位于CPU中,程序不能直接对其操作,每个线程都有独立的程序计数器,线程私有内存区域。

---GC。Garbage Collection/Collector,垃圾回收/回收器,用于分配内存确保被引用对象保留在内存中,以及回收不存在引用关系的对象内存,基本算法是分代收集,针对内存区域中的本地方法栈和堆进行回收,新生代、旧生代和长久代采取不同的GC算法。

---java引用。JDK1.2+,采用强、软、弱、虚4种引用来标记不同的对象。
------强引用(Strong Reference)。永远不会被回收的对象。
------软引用(Soft Reference)。可被回收的对象,由JVM内存紧张与否决定。
------弱引用(Weak Reference)。一定需要被回收的对象。
------虚引用(Phantom Reference)。可忽略,用于作跟踪记录,辅助finalize函数使用。

1.2.2 C++内存机制
C/C++内存空间。C/C++内存空间由栈区、堆区、全局/静态存储区、常量存储区和程序代码区组成。
---栈区。存储执行函数的参数和局部变量等,容量有限,效率很高,由程序自动分配和释放。
---堆区。由程序手动分配和释放。C中采用malloc/free,C++中采用new/delete进行分配和释放,堆大小无限制,由OS内存空间大小决定。
---全局/静态存储区。存放全局变量和静态变量等区域。
---常量存储。存放常量等区域,不允许修改。
---程序代码区。存放程序等二进制代码。

堆生长方向。栈是逆生长,先进栈所分配等内存空间地址更大,堆上顺序生长,先进栈所分配等内存空间地址更小。注意:无论是堆还是栈,指针指向的所分配的某一块内存的首地址永远是这块内存中最小的。

1.2.3Android内存管理

1.2.3.1 Android中包括Native和Java两类进程,Native进程基于C/C++实现,是不包含Davlik实例的进程;java进程基于java语言,是运行在Davlik/ART虚拟机上的进程。Android中每个App默认情况下上运行在一个独立进程中,这个独立进程上从Zygote fork出来的VM进程,即每个App运行在独立的VM空间。

1.2.3.2 Davlik与ART.
---Android 4.4以前使用基于Davlik虚拟机的VM,Android 4.4+引入ART,Android 5.0正式将ART作为默认VM.
---Davlik不同于java虚拟机,执行的是dex文件而非class文件,采用JIT技术。在应用启动时,JIT通过进行连续的性能分析来优化程序代码的执行。在程序执行过程中,Dalvik不断地进行将字节码翻译成机器码的工作。
---ART,Android Runtime,引入了AOT(Ahead-Of-Time)预编译技术,提升了GC效率,支持更多开发调试技巧,具有更长续航能力,提升了APP运行性能。

1.2.3.3 内存限制。不同厂商APP内存限制不同,存放在system/build.prop中。可以在ADB Shell环境中采用cat /system/build.prop命令获取。如下为Nexus5,Android6.0手机获取的信息。headpstatize决定堆分配的初始大小,heapgrowthlimit决定受控下的极限堆大小,heapsize决定堆堆最大值(需要manifest中指定android:largeHeap为true。)若要突破heapsize限制,可以创建子进程(android:process)或者使用jni在native heap中申请空间。

1.2.3.4 App应用切换。Android系统不会在用户切换不同应用时做内存交换的操作,相反,Android会把那些不在前台可见的进程放到LRU缓存中,主要便于在应用再次切回时快速响应。该缓存占用一定的内存,对系统性能有一定影响。

1.2.3.5 App进程级别。Android GC时会针对不同进程级别采取优先级别,根据重要程度从大到小依次为前台进程、可见进程、服务进程、后台进程和空进程。

2、内存分析工具


3、泄漏和溢出
分析内存问题的本质是找出内存被谁占用了,找出内存占用大的对象,找出其关联,跟踪GC可达路径,从而定位谁让这个大对象存活着。
3.1 内存溢出(Out of Memory,OOM)
定义:对象内存占用超过了分配内存大小,内存越界,即内存不够了。

原因:
---内存泄漏导致。内存泄漏对象越来越多时,内存泄漏会导致内存溢出。
---大内存对象。如Android中的Bitmap或加载超大图像资源等。

3.2 内存泄漏(Memory Leaks)
定义:由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

通俗点讲就是,程序申请内存后,没有释放已经申请到的内存,始终占用着,内存使用完后没有归还,被分配的对象可达却无用。

3.2.1 Android中常见的内存泄漏
---长时间保持对Activity、Context、View、Drawable和其他对象的引用。
------Activity使用静态成员。建议使用静态的Activity、View等。
------用Context处理Thread、第三方库初始化等异步程序时,这些异步程序等生命周期可能大于Activity的生命周期,导致Activity无法被回收,造成内存泄漏。
------建议与View无关的操作,Context尽量使用Application Context。

---内部类。与非静态内部类相似,持有外部类的引用导致内存泄漏。

---持有对象的时间超出需要的时间/引用对象没有释放(注意持有对象的生命周期)。
------register对象后缺少对应的unregister操作,如广播等。
------集合对象未清理,资源对象未关闭。如Cursor、File等资源。
------static滥用。当static用于修饰大内存占用对象时,会导致该对象无法回收,造成内存泄漏。
------bitmap使用完后没有回收。

---不良代码。

4、内存性能优化

4.1 内存度量
4.1.1 ActivityManager.MemoryInfo()方法:可以得到当前系统剩余内存及判断是否处于低内存运行,腾讯GT等工具采取的方式。
4.1.2 ActivityManager的getProcessMemoryInfo(int[] pids)方法:得到的MemoryInfo所描述的内存使用情况比较详细,数据的单位所KB.
4.1.3 Debug的getMemoryInfo()、getNativeHeapSize()、getNativeHeapAllocatedSize()、getNativeHeapFreeSize()方法。
4.1.4通过adb相关命令获取,具体有以下几种不同的方法。
---adb shell dumpsys meminfo|grep pkg_name or pid命令,可以直接获取具体进程的内存信息。

---adb shell procrank|grep pkg_name命令,可以获取VSS、RSS、USS、PSS。
------VSS(Virtual Set Size),虚拟耗用内存(包含共享库占用的内存)。
------RSS(Resident Set Size),实际使用的物理内存(包含共享库占用的内存)。
------PSS(Proportional Set Size),实际使用的物理内存(比例分配共享库占用的内存)。
------USS(Unique Set Size),进程独自占用的物理内存(不包含共享库占用的内存)。
一般来说,内存占用大小有如下规律:VSS >= RSS >=PSS >= USS.

---adb shell cat/proc/meminfo命令,可以获取系统整个内存的大致使用情况。
---adb shell px -x命令,可以得到内存信息VSIZE和RSS.

4.2Android与java内存性能优化
---Service的使用。
------尽量少用Service,当后台任务运行完成后,要及时关闭Service,否则由于Service的保持运行状态,导致其占用的内存不会释放。
------用IntentService取代Service,当后台任务完成时,自动结束服务本身。

---UI不可见或内存紧张时,释放内存。在Activity的回调方法onTrimMemory(int level)中,根据level的不同释放内存。
------进程不在缓存中。根据TRIM_MEMORY_RUNNING_MODERATE、TRIM_MEMORY_RUNNING_LOW和TRIM_MEMORY_RUNNING_CRITICAL状态进行处理。
------进程在LRU缓存中,根据TRIM_MEMORY_BACKGROUND、TRIM_MEMORY_MODERATE和TRIM_MEMORY_COMPLETE状态进行处理。

---恰当使用Bitmap。加载Bitmap时尽量保证分辨率和屏幕分辨率对应,大分辨率Bitmap需要进行压缩处理。Android2.3(API 10)以下系统需要手动recycle(Bitmap像素存储在Native内存中)。

---使用SparseArray、SparseBooleanArray和LongSparseArray等优化的数据容器代替HashMap。
---使用static const代替enum。
---非必要情况下,少用抽象。
---对于序列化数据,使用nano protobuf
---尽量少使用依赖注入框架。
---使用ProGuard去除不必要的代码。
---apk打包签名时,使用zipalign工具对齐。
---使用多进程。
---GC主动调用。
---finally调用和重写。
---最后,养成好的编码习惯。

4.3 C/C++常见内存问题
---未初始化的内存和变量。malloc分配的内存不会自动初始化,可在声明的同时进行初始化。
---空指针。使用前先判空,空指针访问会产生segment fault错误。不要忘记为数组和动态内存赋初值。
---野指针。free或delete释放内存后,立即将指针设置为NULL.
---内存覆盖。
---内存越界。避免数组或指针的下标越界,特别要当心发生“多1”或“少1”的操作。
---内存泄漏。动态内存的申请和释放必须配对,防止内存泄漏。

4.4 Android经典内存泄漏实例
如下所示LeakActivity,涉及非静态内部类、匿名内部类等典型内存泄漏场景及正确书写方式。

App架构师实践指南六之性能优化三的更多相关文章

  1. App架构师实践指南四之性能优化一

    App架构师实践指南四之性能优化一     1.性能维度常见用来衡量App性能的维度如图9-1所示.其中,性能指标包括电池(电量/温度).流量(上行流量/下行流量等).CPU(平均/最大/最小).内存 ...

  2. App架构师实践指南五之性能优化二

    App架构师实践指南五之性能优化二 2018年07月30日 13:08:44 nicolelili1 阅读数:214   从UI和CPU方面来说App流畅体验优化,核心为流畅度/卡顿性能优化. 1.基 ...

  3. App架构师实践指南二之App开发工具

    App架构师实践指南二之App开发工具     1.Android Studio 2.编译调试---条件断点.右键单击断点,在弹出的窗口中输入Condition条件.---日志断点.右键单击断点,在弹 ...

  4. App架构师实践指南三之基础组件

    App架构师实践指南三之基础组件 1.基础组件库随着时间的增长,代码量的逐渐积累,新旧项目之间有太多可以服用的代码.下面是整理的公共代码库. 2.关于加密密钥的保护以及网络传输安全是移动应用安全最关键 ...

  5. App架构师实践指南一之App基础语法

    第二章:App基础语法1.编程范式编程范型或编程范式(programming paradigm),是指从事软件工程的一类典型的编程风格.常见的编程范式有过程化(命令行)编程.事件驱动编程.面向对象编程 ...

  6. 菜鸟要做架构师(二)——java性能优化之for循环

    完成同样的功能,用不同的代码来实现,性能上可能会有比较大的差别,所以对于一些性能敏感的模块来说,对代码进行一定的优化还是很有必要的.今天就来说一下java代码优化的事情,今天主要聊一下对于for(wh ...

  7. 网易新闻App架构重构实践:DDD正走向流行

    网易新闻App架构重构实践:DDD正走向流行 https://mp.weixin.qq.com/s/FdwrT_xn3CQqpWoRVBttvQ 小智 InfoQ 2020-05-14 作者 | 小智 ...

  8. 【SQL server初级】数据库性能优化三:程序操作优化

    数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第三部分 数据库性能优化三:程序操作优化 概述:程序访问优化也可以认为是访问SQL语句的优化,一个好的SQL语句是可以减少 ...

  9. MySQL性能优化(三):索引

    原文:MySQL性能优化(三):索引 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/vbi ...

随机推荐

  1. 实现div里的内容垂直居中

    ---恢复内容开始--- 在项目中我们会遇到这种情况,一个div的宽固定,里面的内容长度不定,不管是一行还是多行,都要垂直居中,有俩个实现方法: 1.使用absolute,top:50%,transf ...

  2. 性能测试十二:jmeter进阶之java请求参数化

    如项目中的ip.端口号之类的,都可以在此代码中定义 public Arguments getDefaultParameters() { // TODO Auto-generated method st ...

  3. JavaScript常见的真值

    值 说明 var a =true  值等于true: var a = 1 非0的数字 var a =“hello” 有内容的字符串 var a=20/5 运算结果非0 var a='true' 有内容 ...

  4. 2017-2018-2 20155309南皓芯 Exp6 信息搜集与漏洞扫描

    实践内容 1.各种搜索技巧的应用 2.DNS IP注册信息的查询 3.基本的扫描技术:主机发现.端口扫描.OS及服务版本探测.具体服务的查点 4.漏洞扫描:会扫,会看报告,会查漏洞说明,会修补漏洞 基 ...

  5. DOM编程艺术推荐的addLoadEvent和insertAfter

    addLoadEvent.js function addLoadEvent(func){ var oldonLoad = window.onload; if(typeof window.onload! ...

  6. 华为交换机SNMP OID

    http://vbb.fyjy.net:88/showthread.php?t=4647

  7. 【开源小软件 】Bing每日壁纸 让桌面壁纸保持更新

    发布一个开源小软件,Bing每日壁纸. 该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 欢迎大家下载使用,点star!有问题请留言或者提issue. 开源地 ...

  8. Einbahnstrasse HDU2923

    基础2923题 处理输入很麻烦 有可能一个城市有多辆破车要拖   应该严谨一点的 考虑所有情况 #include<bits/stdc++.h> using namespace std; ] ...

  9. Pytorch 基础

    Pytorch 1.0.0 学习笔记: Pytorch 的学习可以参考:Welcome to PyTorch Tutorials Pytorch 是什么? 快速上手 Pytorch! Tensors( ...

  10. 不一样的go语言-一样的语法

    前言   上一篇入门篇算是初识庐山真面目,我们知道了一个go程序的构成,在这里总结一下. //包名 package //导入包 import "fmt" //main方法,程序入口 ...