文档章节

ANDROID内存优化(大汇总——上)

蜗牛崛起
 蜗牛崛起
发布于 2016/04/28 15:28
字数 2548
阅读 13
收藏 0

内存简介:


RAM(random access memory)随机存取存储器。说白了就是内存。

一般Java在内存分配时会涉及到以下区域:

寄存器(Registers):速度最快的存储场所,因为寄存器位于处理器内部我们在程序中无法控制

栈(Stack):存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中

堆(Heap):堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器(GC)来管理。

静态域(static field):  静态存储区域就是指在固定的位置存放应用程序运行时一直存在的数据,Java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态的数据变量

常量池(constant pool):虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和floating point常量)和对其他类型,字段和方法的符号引用。

非RAM存储:硬盘等永久存储空间


堆栈特点对比:

由于篇幅原因,下面只简单的介绍一下堆栈的一些特性。

:当定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

:当堆中的new产生数组和对象超出其作用域后,它们不会被释放,只有在没有引用变量指向它们的时候才变成垃圾,不能再被使用。即使这样,所占内存也不会立即释放,而是等待被垃圾回收器收走。这也是Java比较占内存的原因。


存取速度比堆要快,仅次于寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

堆是一个运行时数据区,可以动态地分配内存大小,因此存取速度较慢。也正因为这个特点,堆的生存期不必事先告诉编译器,而且Java的垃圾收集器会自动收走这些不再使用的数据。


栈中的数据可以共享, 它是由编译器完成的,有利于节省空间。

例如:需要定义两个变量int a = 3;int b = 3;

编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并让a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

例如上面栈中a的修改并不会影响到b, 而在堆中一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。


内存耗用名词解析:

VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)

RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)

PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)

USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS


OOM:


内存泄露可以引发很多的问题:

1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)

2.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反之内存占用越小,在后台存在的时间就越长)

3.直接崩溃(OutOfMemoryError)


ANDROID内存面临的问题:

1.有限的堆内存,原始只有16M

2.内存大小消耗等根据设备,操作系统等级,屏幕尺寸的不同而不同

3.程序不能直接控制

4.支持后台多任务处理(multitasking)

5.运行在虚拟机之上


5R:

本文主要通过如下的5R方法来对ANDROID内存进行优化:

1.Reckon(计算)

首先需要知道你的app所消耗内存的情况,知己知彼才能百战不殆

2.Reduce(减少)

消耗更少的资源

3.Reuse(重用)

当第一次使用完以后,尽量给其他的使用

5.Recycle(回收)

返回资源

4.Review(检查)

回顾检查你的程序,看看设计或代码有什么不合理的地方。


Reckon (计算):

了解自己应用的内存使用情况是很有必要的。如果当内存使用过高的话就需要对其进行优化,因为更少的使用内存可以减少ANDROID系统终止我们的进程的几率,也可以提高多任务执行效率和体验效果。

下面从系统内存(system ram)和堆内存(heap)两个方面介绍一些查看和计算内存使用情况的方法:


System Ram(系统内存):

观察和计算系统内存使用情况,可以使用Android提供给我们的两个工具procstatsmeminfo。他们一个侧重于后台的内存使用,另一个是运行时的内存使用。

Process Stats: 

Android 4.4 KitKat 提出了一个新系统服务,叫做procstats。它将帮助你更好的理解你app在后台(background)时的内存使用情况。
Procstats可以去监视你app在一段时间的行为,包括在后台运行了多久,并在此段时间使用了多少内存。从而帮助你快速的找到应用中不效率和不规范的地方去避免影响其performs,尤其是在低内存的设备上运行时。
你可以通过adb shell命令去使用procstats(adb shell dumpsys procstats --hours 3,或者更方便的方式是运行Process Stats开发者工具(在4.4版本的手机中点击Settings > Developer options > Process Stats

点击单个条目还可以查看详细信息

meminfo:
Android还提供了一个工具叫做meminfo。 它是根据PSS标准 (Proportional Set Size——实际物理内存)计算每个进程的内存使用并且按照重要程度排序。
你可以通过命令行去执行它:( adb shell dumpsys meminfo)或者使用在设备上点击Settings > Apps > Running(与Procstats不用,它也可以在老版本上运行)

更多关于Procstatsmeninfo的介绍可以参考我翻译的一篇文章:Process Stats:了解你的APP如何使用内存


Heap(堆内存):

在程序中可以使用如下的方法去查询内存使用情况


ActivityManager#getMemoryClass()

查询可用堆内存的限制

3.0(HoneyComb)以上的版本可以通过largeHeap=“true”来申请更多的堆内存(不过这算作“作弊”)


ActivityManager#getMemoryInfo(ActivityManager.MemoryInfo)
得到的MemoryInfo中可以查看如下Field的属性:
availMem:表示系统剩余内存
lowMemory:它是boolean值,表示系统是否处于低内存运行
hreshold:它表示当系统剩余内存低于好多时就看成低内存运行

android.os.Debug#getMemoryInfo(Debug.MemoryInfo memoryInfo)

得到的MemoryInfo中可以查看如下Field的属性:

dalvikPrivateDirty :  The private dirty pages used by dalvik。
dalvikPss  : The proportional set size for dalvik.
dalvikSharedDirty  The shared dirty pages used by dalvik.
nativePrivateDirty  The private dirty pages used by the  native heap .
nativePss  The proportional set size for the native heap.
nativeSharedDirty  : The shared dirty pages used by the  native heap.
otherPrivateDirty  The private dirty pages used by everything else.
otherPss  : The proportional set size for everything else.
otherSharedDirty  : The shared dirty pages used by everything else.

dalvik是指 dalvik所使用的内存
native是被native堆使用的内存。应该指使用C\C++在堆上分配的 内存
other:是指除 dalvik和 native使用的内存。但是具体是指什么呢?至少包括在C\C++分配的非堆内存,比如分配在栈上的内存。
private:是指私有的。非共享的。
share:是指共享的内存
PSS 实际使用的物理内存(比例分配共享库占用的内存)
 PrivateDirty它是指非共享的,又不能换页出去( can not be paged to disk )的内存的大小。比如Linux为了提高分配内存速度而缓冲的小对象,即使你的进程结束,该内存也不会释放掉,它只是又重新回到缓冲中而已。
SharedDirty:参照 PrivateDirty我认为 它应该是指共享的,又不能换页出去( can not be paged to disk )的内存的大小。比如Linux为了提高分配内存速度而缓冲的小对象,即使所有共享它的进程结束,该内存也不会释放掉,它只是又重新回到缓冲中而已。

android.os.Debug#getNativeHeapSize()

返回的是当前进程navtive堆本身总的内存大小

android.os.Debug#getNativeHeapAllocatedSize()

返回的是当前进程navtive堆中已使用的内存大小

android.os.Debug#getNativeHeapFreeSize()

返回的是当前进程navtive堆中已经剩余的内存大小


Memory Analysis Tool(MAT):

通常内存泄露分析被认为是一件很有难度的工作,一般由团队中的资深人士进行。不过,今天我们要介绍的 MAT(Eclipse Memory Analyzer)被认为是一个“傻瓜式“的堆转储文件分析工具,你只需要轻轻点击一下鼠标就可以生成一个专业的分析报告。

如下图:

关于详细的MAT使用我推荐下面这篇文章:使用 Eclipse Memory Analyzer 进行堆转储文件分析

本文转载自:http://blog.csdn.net/a396901990/article/details/37914465

蜗牛崛起
粉丝 3
博文 127
码字总数 63547
作品 0
东城
程序员
私信 提问
2017微信数据报告:日活跃用户达9亿、日发消息380亿条

1、引言 2017年11月9日,微信团队在成都腾讯全球合作伙伴大会上为全球伙伴解读了最新的《2017微信数据报告》。微信每天有多少条消息被发送?目前有多少个行业已经在使用小程序了?答案尽在其...

JackJiang-
2017/11/09
0
0
为什么 Android 手机总是越用越慢?

根据第三方的调研数据显示,有77%的Android手机用户承认自己曾遭遇过手机变慢的影响,百度搜索“Android+卡慢”,也有超过460万条结果。在业内,Android手机一直有着“越用越慢”的口碑,这个...

oschina
2014/08/27
7.6K
38
Android 8.1 开发者预览版发布,优化内存支持

今晨,谷歌推出了 Android 8.1 首个开发者预览版,此次升级涵盖了针对多个功能的提升优化,其中包含对 Android Go (设备运行内存小于等于 1 GB)和加速设备上对机器学习的全新神经网络 API(...

王练
2017/10/26
2K
10
Android外包开发

我们的能力: 1、我们独立完成移动终端产品Android版的开发、测试、发布; 2. 我们可进行移动终端产品技术架构的设计; 3. 我们负责对手机软件系列产品进行维护和持续升级; 4. 我们依据项目...

广州缘创信息科技
2016/04/06
9
0
性能优化之Java(Android)代码优化

最新最准确内容建议直接访问原文:性能优化之Java(Android)代码优化 本文为Android性能优化的第三篇——Java(Android)代码优化。主要介绍Java代码中性能优化方式及网络优化,包括缓存、异步、...

Trinea
2013/08/26
2.6K
1

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周一乱弹 —— 年迈渔夫遭黑帮袭抢

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享Elvis Presley的单曲《White Christmas》: 《White Christmas》- Elvis Presley 手机党少年们想听歌,请使劲...

小小编辑
今天
2.1K
20
CentOS7.6中安装使用fcitx框架

内容目录 一、为什么要使用fcitx?二、安装fcitx框架三、安装搜狗输入法 一、为什么要使用fcitx? Gnome3桌面自带的输入法框架为ibus,而在使用ibus时会时不时出现卡顿无法输入的现象。 搜狗和...

技术训练营
昨天
10
0
《Designing.Data-Intensive.Applications》笔记 四

第九章 一致性与共识 分布式系统最重要的的抽象之一是共识(consensus):让所有的节点对某件事达成一致。 最终一致性(eventual consistency)只提供较弱的保证,需要探索更高的一致性保证(stro...

丰田破产标志
昨天
12
0
docker 使用mysql

1, 进入容器 比如 myslq1 里面进行操作 docker exec -it mysql1 /bin/bash 2. 退出 容器 交互: exit 3. mysql 启动在容器里面,并且 可以本地连接mysql docker run --name mysql1 --env MY...

之渊
昨天
19
0
python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部