前言
在工作这几年,我一直深受内存问题的困扰,在和内存的不断抗争中,我逐渐积累了一些内存的知识,接来下来我会用几篇文章简单记录一下这几年的我学到的内存相关的经验。另外,本系列文章不去过多的分析Linux底层代码,只是探讨遇到内存问题时的解决方法论。 以下是全部文章的标题和链接:
- Android Memory(一) -- 内存基础知识
- Android Memory(二) -- 应用内存占用分析
- Android Memory(三) -- 问题原因分析
- Android Memory(四) -- 问题定位&解决方案1
- Android Memory(五) -- 问题定位&解决方案2
- Android Memory(六) -- 内存日常监控
1. Android内存分布
Linux为每个进程维护一个单独的虚拟地址空间,Linux的进程有32位和64位,32位进程的虚拟地址空间是232=4G, 64位进程的虚拟地址空间为239=512G。我们以32位的进程为例,介绍一下其内存模型,下边是一张32位的内存分布图:
其中对用户可用的虚拟地址空间大小为3G, 是各个进程独立的。 内核空间占用大小为1G,包括内核中的代码和数据结构,是多个进程共享的。
Linux将虚拟内存以段的形式组织起来,其中包括代码段(.text)、数据段(.data和.bss)、堆、共享库等。其中这里边有两部分是我们着重关注的,这两部分就是heap和代码段。
- 堆:我们平常写代码使用的malloc、free、mmap、munmap等,都是操作的这一部分,所以这一部分一般也是内存泄漏的重灾区。
- .text 代码段,我们的包大小增加主要是增加的这一部分的大小,所以google给我们建议,不要忽略包大小对内存带来的影响,有的时候我们使劲优化内存使用,还不如去专心做一下包大小优化来的快。
另外上边几个段的都是干什么用的,我们就不去详细探究了,如果感兴趣,请查阅 <<深入理解计算机系统>> 这本书。
2. 进程虚拟内存空间和物理内存空间的映射
下图展示了进程虚拟内存和物理内存之间的映射:
3. 内存耗用:VSS/RSS/PSS/USS
- VSS - Virtual Set Size 进程使用的虚拟内存,最大值和进程是32位还是64位相关。此部分内存包含为内核分配的内存,应用运行时分配的内存、应用本身的资源占用的大小如包内的dex产生的oat文件、包内的so文件、ttf文件等。另外此部分内存还包含部分分配的还未使用的内存,比如我们写了一句代码:
int m[10] = new int[10];
m[0] = 100;
m[1] = 100;
此时,只有m[0]和m[1]进行了数值写入,此段代码使用的虚拟内存为sizeof(int) * 10, 使用的物理内存为sizeof(int) * 2。
- RSS - Resident Set Size RSS表示自己使用的物理内存 + 共享库使用的物理内存 。这种计算方式就有些不合理,比如libc.so, 被多个进程使用,但是每个进程的RSS计算的时候,都会把共享库所占用的内存完整的计算进去。但是不管有多少个进程使用该共享库,该共享库仅被加载到内存一次,所以此种方式把共享库占用的内存计算多了。 RSS并不能准确反映单进程的内存占用情况 。
- PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存,按照进程数等比例划分).
- USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存) 。USS是非常非常有用的数据,因为它反映了运行一个特定进程真实的边际成本(增量成本)。当一个进程被销毁后,USS是真实返回给系统的内存。 当进程中存在一个可疑的内存泄露时,USS是最佳观察数据。
4. Android中的进程
- Native进程:采用C/C++实现,不包含dalvik实例的Linux进程,/system/bin/目录下面的程序文件运行后都是以native进程形式存在的。
- Java进程:实例化了dalvik虚拟机实例的linux进程,进程的入口main函数为java函数。dalvik虚拟机实例的宿主进程是fork()系统调用创建的linux进程,所以每一个android上的java进程实际上就是一个linux进程,只是进程中多了一个dalvik虚拟机实例。
5. Android中获取内存信息
- Java代码获取内存信息:
private void getMaxMemoryInfo() {
Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.e("XMemory", "Dalvik MaxMemory:" + Long.toString(maxMemory / (1024 * 1024)));
ActivityManager activityManager = (ActivityManager) ContextHolder.getAppContext().getSystemService(Context.ACTIVITY_SERVICE);
Log.e("XMemory", "Dalvik MemoryClass:" + Long.toString(activityManager.getMemoryClass()));
Log.e("XMemory", "Dalvik LargeMemoryClass:" + Long.toString(activityManager.getLargeMemoryClass()));
ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(info);
Log.e("XMemory", "系统总内存:" + (info.totalMem / (1024 * 1024)) + "M");
Log.e("XMemory", "系统剩余内存:" + (info.availMem / (1024 * 1024)) + "M");
Log.e("XMemory", "系统是否处于低内存运行:" + info.lowMemory);
Log.e("XMemory", "系统剩余内存低于" + (info.threshold / (1024 * 1024)) + "M时为低内存运行");
}
- 通过名命令行获取内存信息:
dalvik.vm.heapstartsize
App启动后, 系统分配给它的Heap初始大小. 随着App使用可增加.
dalvik.vm.heapgrowthlimit
默认情况下, App可使用的Heap的最大值, 超过这个值就会产生OOM.
dalvik.vm.heapsize
如果App的manifest文件中配置了largeHeap属性, 如下.则App可使用的Heap的最大值为此项设定值.
dalvik.vm.heaptargetutilization
当前理想的堆内存利用率. GC后, Dalvik的Heap内存会进行相应的调整, 调整到当前存活的对象的大小和 / Heap大小 接近这个选项的值, 即这里的0.75. 注意, 这只是一个参考值.
dalvik.vm.heapminfree
单次Heap内存调整的最小值.
dalvik.vm.heapmaxfree
单次Heap内存调整的最大值.
参考相关文章:
- blog.csdn.net/weixin_3285…
- 深入理解计算机系统