精华内容
下载资源
问答
  • android虚拟内存

    2015-05-09 14:27:46
    亲测红米1可用 1.使用root大师root 2.安装Swapper2,设定大小位置,自动启动 3.设定自动启动,红米->安全中心->授权管理->自动启动应用管理->手工添加->找到Swapper2 4.查看效果cmd-> adb shell -> busybox free ...
  • 例如,linux内核的虚拟内存本质上是一个交换文件,并没有对单个进程大小设置限制,这会在文件已满时导致“颠簸”或无休止的交换。与之形成鲜明对比的是,jvm(它只是另一个linux进程)为其应用程序设置了一个共同的...

    Linux内核和jvm应该被视为苹果和橘子。混淆两者可能是灾难性的。

    例如,linux内核的虚拟内存本质上是一个交换文件,并没有对单个进程大小设置限制,这会在文件已满时导致“颠簸”或无休止的交换。

    与之形成鲜明对比的是,jvm(它只是另一个linux进程)为其应用程序设置了一个共同的最大大小(例如256MB)。 Android应用程序通常在自己的jvms中运行,防止任何给定的jvm“颠簸”。相反,GC将抛出一个OOM并退出。

    当内存虚拟内存耗尽时,Android将进一步杀掉应用程序(包含活动线程组的任务的jvms),如果需要,稍后重新启动它们。它仍然可以冻结(捶打),但不是经常冻结,例如。无人值守的Linux数据库或Web服务器。

    颠簸(冻结)android的解决方案与颠簸的linux服务器完全相同。弹跳(循环动力)。因为它是一个linux服务器。

    考虑到Android已经为全世界提供了数百万个完全连接但大多数空闲无处不在的Linux服务器,这些服务器可以容纳无数群多线程进程(jvms只有一种类型)。

    它拥有终极超级计算机的骨骼,甚至使最精细的数据中心相形见绌。

    甚至没有提到大多数机器人都有并行处理器(GPU)比它们的CPU快100倍,再次只是闲置。除了一些知道如何解雇他们的游戏玩家。

    仅作为概述,这个帖子上已有很好的链接。

    展开全文
  • 操作系统对内存的管理 没有内存抽象的年代 在早些的操作系统中,并没有引入内存抽象的概念。程序直接访问和操作的都是物理内存。比如当执行如下指令时:mov reg1,1000 这条指令会毫无想象力的将物理地址1000中的...

    操作系统对内存的管理

    没有内存抽象的年代

    在早些的操作系统中,并没有引入内存抽象的概念。程序直接访问和操作的都是物理内存。比如当执行如下指令时:mov reg1,1000

    这条指令会毫无想象力的将物理地址1000中的内容赋值给寄存器。不难想象,这种内存操作方式使得操作系统中存在多进程变得完全不可能,比如MS-DOS,你必须执行完一条指令后才能接着执行下一条。如果是多进程的话,由于直接操作物理内存地址,当一个进程给内存地址1000赋值后,另一个进程也同样给内存地址赋值,那么第二个进程对内存的赋值会覆盖第一个进程所赋的值,这回造成两条进程同时崩溃。

    没有内存抽象对于内存的管理通常非常简单,除去操作系统所用的内存之外,全部给用户程序使用。或是在内存中多留一片区域给驱动程序使用

    无内存抽象存在的问题

    1. 用户程序可以访问任意内存,容易破坏操作系统,造成崩溃

    2. 同时运行多个程序特别困难

    内存抽象:地址空间

    1. 基址寄存器与界限寄存器可以简单的动态重定位,每个内存地址送到内存之前,都会自动加上基址寄存器的内容。

    2. 交换技术把一个进程完全调入内存,使该进程运行一段时间,然后把它存回磁盘。空闲进程主要存在磁盘上,所以当他们不运行时就不会占用内存。

    为什么要有地址空间?

    首先直接把物理地址暴露给进程会带来严重问题

    1. 如果用户程序可以寻址内存的每个字节,就有很大的可能破坏操作系统,造成系统崩溃

    2. 同时运行多个程序十分困难 地址空间创造了一个新的内存抽象,地址空间是一个进程可用于寻址内存的一套地址的集合。每个进程都有一个自己的地址空间,并且这个地址空间独立于其它进程的地址空间。使用基址寄存器和界限器可以实现。

    虚拟内存

    虚拟内存是现代操作系统普遍使用的一种技术。前面所讲的抽象满足了多进程的要求,但很多情况下,现有内存无法满足仅仅一个大进程的内存要求(比如很多游戏,都是10G+的级别)。在早期的操作系统曾使用覆盖(overlays)来解决这个问题,将一个程序分为多个块,基本思想是先将块0加入内存,块0执行完后,将块1加入内存。依次往复,这个解决方案最大的问题是需要程序员去程序进行分块,这是一个费时费力让人痛苦不堪的过程。后来这个解决方案的修正版就是虚拟内存。

    虚拟内存的基本思想是,每个进程有用独立的逻辑地址空间,内存被分为大小相等的多个块,称为(Page).每个页都是一段连续的地址。对于进程来看,逻辑上貌似有很多内存空间,其中一部分对应物理内存上的一块(称为页框,通常页和页框大小相等),还有一些没加载在内存中的对应在硬盘上。

    由上图可以看出,虚拟内存实际上可以比物理内存大。当访问虚拟内存时,会通过MMU(内存管理单元)去匹配对应的物理地址,而如果虚拟内存的页并不存在于物理内存中,会产生缺页中断,从磁盘中取得缺的页放入内存,如果内存已满,还会根据某种算法将磁盘中的页换出。

    虚拟内存和物理内存的匹配是通过页表实现,页表存在MMU中,页表中每个项通常为32位,既4byte,除了存储虚拟地址和页框地址之外,还会存储一些标志位,比如是否缺页,是否修改过,写保护等。可以把MMU想象成一个接收虚拟地址项返回物理地址的方法。

    因为页表中每个条目是4字节,现在的32位操作系统虚拟地址空间会是2的32次方,即使每页分为4K,也需要2的20次方 * 4字节 = 4M的空间,为每个进程建立一个4M的页表并不明智。因此在页表的概念上进行推广,产生二级页表,二级页表每个对应4M的虚拟地址,而一级页表去索引这些二级页表,因此32位的系统需要1024个二级页表,虽然页表条目没有减少,但内存中可以仅仅存放需要使用的二级页表和一级页表,大大减少了内存的使用。

    引入多级页表的原因是避免把全部页表一直存在内存中

    虚拟地址和物理地址匹配规则

    虚拟页号可用做页表的索引,以找到该虚拟页面对应页表项。由页表项可以找到页框号。然后把页框号拼接到偏移量的高位端,以替换虚拟页号,形成送往内存的物理地址。

    页表的目的是把虚拟页面映射为页框,从数学的角度来说,页表是一个函数,它的参数是,虚拟页号,结果是物理页框号。通过这个函数可以把虚拟地址中的虚拟页面域替换为页框域,从而形成物理地址。

    页面置换算法

    地址映射过程中,若在页面中发现所要访问的页面不再内存中,则产生缺页中断。当发生缺页中断时操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。如果要换出的页面在内存驻留期间已经被修改过,就必须把它写回磁盘以更新该页面在磁盘的副本;如果该页面没有被修改过,那么它在磁盘上的副本已经是最新的,不需要回写。直接用调入的页面覆盖掉被淘汰的页面就可以了。而用来选择淘汰哪一页的规则叫做页面置换算法。

    因为在计算机系统中,读取少量数据硬盘通常需要几毫秒,而内存中仅仅需要几纳秒。一条CPU指令也通常是几纳秒,如果在执行CPU指令时,产生几次缺页中断,那性能可想而知,因此尽量减少从硬盘的读取无疑是大大的提升了性能。而前面知道,物理内存是极其有限的,当虚拟内存所求的页不在物理内存中时,将需要将物理内存中的页替换出去,选择哪些页替换出去就显得尤为重要,如果算法不好将未来需要使用的页替换出去,则以后使用时还需要替换进来,这无疑是降低效率的,让我们来看几种页面替换算法。

    最优页面置换算法(Optimal Page Replacement Algorithm)

    最饥饿页面置换算法是将未来最久不使用的页替换出去,这听起来很简单,但是无法实现。根据页面被访问前所需要的指令数作为标记,根据指令数的由多到少进行置换,这个方法对评价页面置换算法很有用,但它在实际系统中却不能使用,因为无法真正的实现。这种算法可以作为衡量其它算法的基准。

    最近最少使用页面置换算法(Least Recently Used)

    通常在前几条指令中使用频繁的页面很可能在后面几条指令中页频繁使用。LRU算法就是在缺页发生时首先置换最长时间未被使用的页面。优秀但是难以实现。

    最近未使用页面置换算法(Not Recently Used Replacement Algorithm)

    在最近的一个时钟周期内,淘汰一个没有被访问的已修改页面,近似 LRU 算法,NRU 只是更粗略些。

    这种算法给每个页一个标志位,R表示最近被访问过,M表示被修改过。定期对R进行清零。这个算法的思路是首先淘汰那些未被访问过R=0的页,其次是被访问过R=1,未被修改过M=0的页,最后是R=1,M=1的页。

    先进先出的页面置换算法(First-In First-Out Page Replacement Algorithm)

    这种算法的思想是淘汰在内存中最久的页,这种算法的性能接近于随机淘汰。可能抛弃重要的页面,并不好。

    第二次机会页面置换算法(Second Chance Page Replacement Algorithm)

    这种算法是在FIFO的基础上,为了避免置换出经常使用的页,增加一个标志位R,如果最近使用过将R置1,当页将会淘汰时,如果R为1,则不淘汰页,将R置0.而那些R=0的页将被淘汰时,直接淘汰。这种算法避免了经常被使用的页被淘汰。

    时钟替换算法(Clock Page Replacement Algorithm)

    虽然改进型FIFO算法避免置换出常用的页,但由于需要经常移动页,效率并不高。因此在改进型FIFO算法的基础上,将队列首位相连形成一个环路,当缺页中断产生时,从当前位置开始找R=0的页,而所经过的R=1的页被置0,并不需要移动页。

    下表是上面几种算法的简单比较:

    算法 描述
    最佳置换算法 无法实现,最为测试基准使用
    最近不常使用算法 和LRU性能差不多
    先进先出算法 有可能会置换出经常使用的页
    改进型先进先出算法 和先进先出相比有很大提升
    最久未使用算法 性能非常好,但实现起来比较困难
    时钟置换算法 非常实用的算法

    分页系统中的设计问题

    1. 在任何分页式系统中,都需要考虑两个主要的问题:虚拟地址到到物理地址的映射必须非常快;如果虚拟地址空间很大,页表也会很大。

    2. 局部分分配策略与全局分配策略,怎样在相互竞争的可运行进程之间分配内存.

      • 局部分配为每个进程分配固定的内存片段,即使有大量的空闲页框存在,工作集的增长也会颠簸。

      • 全局分配在进程间动态地分配页框,分配给各个进程的页框数是动态变化的。给每个进程分配一个最小的页框数使无论多么小的进程都可以运行,再需要更大的内存时去公共的内存池里去取。

      • FIFO LRU 既适用于局部算法,也适用于全局算法。WSClock 工作集更适用于局部算法。

    3. 负载控制 , 即使使用了最优的页面置换算法,最理想的全局分配。当进程组合的工作集超出内存容量时,就可能发生颠簸。这时只能根据进程的特性(IO 密集 or CPU 密集)将进程交换到磁盘上。

    4. 页面大小 的确定不存在全局最优的结果,小页面减少页面内内存浪费,但是小页面,意味着更大的页表,更多的计算转换时间。现在一般的页面大小是 4KB 或 8KB

    5. 地址空间太小,所以分离指令空间,数据空间

    6. 共享页面,在数据空间和指令空间分离的基础上很容易实现程序的共享,linux 采取了 copy on write 的方案,也有数据的共享。

    7. 共享库,多个程序并发,如果有一个程序已经装在了共享库,其他程序就没有必要再进行装载,减少内存浪费。而且共享库并不会一次性的装入内存,而是根据需要以页面为单位进行装载的。共享时不使用绝对地址,使用相对偏移量的代码(位置无关代码 position-independent code)

    8. 共享库是内存映射文件的一种特例,核心思想是进程可以发起一个系统调用,将一个文件映射到其虚拟地址空间的一部分,在多数实现中在映射共享的页面时不会实际读入页面的内容,而是在访问时被每次一页的读入,磁盘文件被当作后背存储。当进程退出或显示的接触文件时,所有被改动的页面会被写入到文件中。

    9. 清除策略,发生缺页中断时有大量的空闲页框,此时的分页系统在最佳状态,有一个分页守护(paging daemon)的后台进程,它在多数时候睡眠,但会被定期唤醒,如果空闲页框过少,分页守护进程通过预定的页面置换算法选择页面换出内存。

    10. 虚拟内存接口,在一些高级系统中,程序员可以对内存映射进行控制,允许控制的原因是为了允许两个或多个进程共享一部分内存。页面共享可以用来实现高性能的消息传递系统。

    Linux内存管理

    内核空间

    页(page)是内核的内存管理的基本单位

    struct page {
     page_flags_t flags;  页标志符
     atomic_t _count;    页引用计数
     atomic_t _mapcount;     页映射计数
     unsigned long private;    私有数据指针
     struct address_space *mapping;    该页所在地址空间描述结构指针,用于内容为文件的页帧
     pgoff_t index;               该页描述结构在地址空间radix树page_tree中的对象索引号即页号
     struct list_head lru;        最近最久未使用struct slab结构指针链表头变量
     void *virtual;               页虚拟地址
    };
    
    • flags:页标志包含是不是脏的,是否被锁定等等,每一位单独表示一种状态,可同时表示出32种不同状态,定义在<linux/page-flags.h>

    • _count:计数值为-1表示未被使用。

    • virtual:页在虚拟内存中的地址,对于不能永久映射到内核空间的内存(比如高端内存),该值为NULL;需要事必须动态映射这些内存。

    尽管处理器的最小可寻址单位通常为字或字节,但内存管理单元(MMU,把虚拟地址转换为物理地址的硬件设备)通常以页为单位处理。内核用struct page结构体表示每个物理页,struct page结构体占40个字节,假定系统物理页大小为4KB,对于4GB物理内存,1M个页面,故所有的页面page结构体共占有内存大小为40MB,相对系统4G,这个代价并不高。

    内核把页划分在不同的区(zone)

    总共3个区,具体如下:

    描述 物理内存(MB)
    ZONE_DMA DMA使用的页 <16
    ZONE_NORMAL 可正常寻址的页 16 ~896
    ZONE_HIGHMEM 动态映射的页 >896
    • 执行DMA操作的内存必须从ZONE_DMA区分配

    • 一般内存,既可从ZONE_DMA,也可从ZONE_NORMAL分配,但不能同时从两个区分配;

    用户空间

    用户空间中进程的内存,往往称为进程地址空间

    Linux采用虚拟内存技术。进程的内存空间只是虚拟内存(或者叫作逻辑内存),而程序的运行需要的是实实在在的内存,即物理内存(RAM)。在必要时,操作系统会将程序运行中申请的内存(虚拟内存)映射到RAM,让进程能够使用物理内存。

    地址空间

    每个进程都有一个32位或64位的地址空间,取决于体系结构。 一个进程的地址空间与另一个进程的地址空间即使有相同的内存地址,也彼此互不相干,对于这种共享地址空间的进程称之为线程。一个进程可寻址4GB的虚拟内存(32位地址空间中),但不是所有虚拟地址都有权访问。对于进程可访问的地址空间称为内存区域。每个内存区域都具有对相关进程的可读、可写、可执行属性等相关权限设置。

    内存区域可包含的对象:

    • 代码段(text section): 可执行文件代码

    • 数据段(data section): 可执行文件的已初始化全局变量(静态分配的变量和全局变量)。

    • bss段:程序中未初始化的全局变量,零页映射(页面的信息全部为0值)。

    • 进程用户空间栈的零页映射(进程的内核栈独立存在并由内核维护)

    • 每一个诸如C库或动态连接程序等共享库的代码段、数据段和bss也会被载入进程的地址空间

    • 任何内存映射文件

    • 任何共享内存段

    • 任何匿名的内存映射(比如由malloc()分配的内存)

    这些内存区域不能相互覆盖,每一个进程都有不同的内存片段。

    内存描述符

    内存描述符由mm_struct结构体表示

    struct mm_struct {
     struct vm_area_struct *mmap;
     rb_root_t mm_rb;
     ...
     atomic_t mm_users;
     atomic_t mm_count;
    ​
     struct list_head mmlist;
     ...
    };
    
    • mm_users:代表正在使用该地址的进程数目,当该值为0时mm_count也变为0;

    • mm_count: 代表mm_struct的主引用计数,当该值为0说明没有任何指向该mm_struct结构体的引用,结构体会被撤销。

    • mmap和mm_rb:描述的对象都是相同的

      • mmap以链表形式存放, 利于高效地遍历所有元素

      • mm_rb以红黑树形式存放,适合搜索指定元素

    • mmlist:所有的mm_struct结构体都通过mmlist连接在一个双向链表中,该链表的首元素是init_mm内存描述符,它代表init进程的地址空间。

    在进程的进程描述符(<linux/sched.h>中定义的task_struct结构体)中,mm域记录该进程使用的内存描述符。故current->mm代表当前进程的内存描述符。

    fork()函数 利用copy_mm函数复制父进程的内存描述符,子进程中的mm_struct结构体通过allcote_mm()从高速缓存中分配得到。通常,每个进程都有唯一的mm_struct结构体,即唯一的进程地址空间。

    当子进程与父进程是共享地址空间,可调用clone(),那么不再调用allcote_mm(),而是仅仅是将mm域指向父进程的mm,即 tsk->mm = current->mm。

    相反地,撤销内存是exit_mm()函数,该函数会进行常规的撤销工作,更新一些统计量。

    内核线程

    • 没有进程地址空间,即内核线程对应的进程描述符中mm=NULL

    • 内核线程直接使用前一个进程的内存描述符,仅仅使用地址空间中和内核内存相关的信息

    页表

    应用程序操作的对象时映射到物理内存之上的虚拟内存,而处理器直接操作的是物理内存。故应用程序访问一个虚拟地址时,需要将虚拟地址转换为物理地址,然后处理器才能解析地址访问请求,这个转换工作通过查询页表完成。

    Linux使用三级页表完成地址转换。

    1. 顶级页表:页全局目录(PGD),指向二级页目录;

    2. 二级页表:中间页目录(PMD),指向PTE中的表项;

    3. 最后一级:页表(PTE),指向物理页面。

    多数体系结构,搜索页表工作由硬件完成。每个进程都有自己的页表(线程会共享页表)。为了加快搜索,实现了翻译后缓冲器(TLB),作为将虚拟地址映射到物理地址的硬件缓存。还有写时拷贝方式共享页表,当fork()时,父子进程共享页表,只有当子进程或父进程试图修改特定页表项时,内核才创建该页表项的新拷贝,之后父子进程不再共享该页表项。可见,利用共享页表可以消除fork()操作中页表拷贝所带来的消耗。

    进程与内存

    所有进程都必须占用一定数量的内存,这些内存用来存放从磁盘载入的程序代码,或存放来自用户输入的数据等。内存可以提前静态分配和统一回收,也可以按需动态分配和回收。

    对于普通进程对应的内存空间包含5种不同的数据区:

    • 代码段

    • 数据段

    • BSS段

    • 堆:动态分配的内存段,大小不固定,可动态扩张(malloc等函数分配内存),或动态缩减(free等函数释放);

    • 栈:存放临时创建的局部变量;

    进程内存空间

    Linux采用虚拟内存管理技术,每个进程都有各自独立的进程地址空间(即4G的线性虚拟空间),无法直接访问物理内存。这样起到保护操作系统,并且让用户程序可使用比实际物理内存更大的地址空间。

    • 4G进程地址空间被划分两部分,内核空间和用户空间。用户空间从0到3G,内核空间从3G到4G;

    • 用户进程通常情况只能访问用户空间的虚拟地址,不能访问内核空间虚拟地址。只有用户进程进行系统调用(代表用户进程在内核态执行)等情况可访问到内核空间;

    • 用户空间对应进程,所以当进程切换,用户空间也会跟着变化;

    • 内核空间是由内核负责映射,不会跟着进程变化;内核空间地址有自己对应的页表,用户进程各自有不同额页表。

    内存分配

    进程分配内存,陷入内核态分别由brk和mmap完成,但这两种分配还没有分配真正的物理内存,真正分配在后面会讲。

    • brk: 数据段的最高地址指针_edata往高地址推

      • 当malloc需要分配的内存<M_MMAP_THRESHOLD(默认128k)时,采用brk;

      • brk分配的内存需高地址内存全部释放之后才会释放。(由于是通过推动指针方式)

      • 当最高地址空间的空闲内存大于M_TRIM_THRESHOLD时(默认128k),执行内存紧缩操作;

    • do_mmap:在堆栈中间的文件映射区域找空闲的虚拟内存

      • 当malloc需要分配的内存>M_MMAP_THRESHOLD(默认128k)时,采用do_map();

      • mmap分配的内存可以单独释放

    物理内存

    • 物理内存只有进程真正去访问虚拟地址,发生缺页中断时,才分配实际的物理页面,建立物理内存和虚拟内存的映射关系。

    • 应用程序操作的是虚拟内存;而处理器直接操作的却是物理内存。当应用程序访问虚拟地址,必须将虚拟地址转化为物理地址,处理器才能解析地址访问请求。

    • 物理内存是通过分页机制实现的

    • 物理页在系统中由也结构struct page描述,所有的page都存储在数组mem_map[]中,可通过该数组找到系统中的每一页。

    虚拟内存 转化为 真实物理内存:

    • 虚拟进程空间:通过查询进程页表,获取实际物理内存地址;

    • 虚拟内核空间:通过查询内核页表,获取实际物理内存地址;

    • 物理内存映射区:物理内存映射区与实际物理去偏移量仅PAGE_OFFSET,通过通过virt_to_phys()转化;

    虚拟内存与真实物理内存映射关系:

    其中物理地址空间中除了896M(ZONE_DMA + ZONE_NORMAL)的区域是绝对的物理连续,其他内存都不是物理内存连续。在虚拟内核地址空间中的安全保护区域的指针都是非法的,用于保证指针非法越界类的操作,vm_struct是连续的虚拟内核空间,对应的物理页面可以不连续,地址范围(3G + 896M + 8M) ~ 4G;另外在虚拟用户空间中 vm_area_struct同样也是一块连续的虚拟进程空间,地址空间范围0~3G。

    碎片问题

    • 外部碎片:未被分配的内存,由于太多零碎的不连续小内存,无法满足当前较大内存的申请要求;

      • 原因:频繁的分配与回收物理页导致大量的小块内存夹杂在已分配页面中间;

      • 解决方案:伙伴算法有所改善

    • 内部碎片:已经分配的内存,却不能被利用的内存空间;

      • 缘由:所有内存分配必须起始可被4、8或16(体系结构决定)整除的地址或者MMU分页机制限制;

      • 解决方案:slab分配器有所改善

      • 实例:请求一个11Byte的内存块,系统可能会分配12Byte、16Byte等稍大一些的字节,这些多余空间就产生碎片

    Android进程内存管理

    进程的地址空间

    在32位操作系统中,进程的地址空间为0到4GB,

    示意图如下:

    这里主要说明一下Stack和Heap:

    Stack空间(进栈和出栈)由操作系统控制,其中主要存储函数地址、函数参数、局部变量等等,所以Stack空间不需要很大,一般为几MB大小。

    Heap空间的使用由程序员控制,程序员可以使用malloc、new、free、delete等函数调用来操作这片地址空间。Heap为程序完成各种复杂任务提供内存空间,所以空间比较大,一般为几百MB到几GB。正是因为Heap空间由程序员管理,所以容易出现使用不当导致严重问题。

    进程内存空间和RAM之间的关系

    进程的内存空间只是虚拟内存(或者叫作逻辑内存),而程序的运行需要的是实实在在的内存,即物理内存(RAM)。在必要时,操作系统会将程序运行中申请的内存(虚拟内存)映射到RAM,让进程能够使用物理内存。

    RAM作为进程运行不可或缺的资源,对系统性能和稳定性有着决定性影响。另外,RAM的一部分被操作系统留作他用,比如显存等等,内存映射和显存等都是由操作系统控制,我们也不必过多地关注它,进程所操作的空间都是虚拟地址空间,无法直接操作RAM。

    示意图如下:

    Android中的进程

    Native进程:采用C/C++实现,不包含Dalvik实例的Linux进程,/system/bin/目录下面的程序文件运行后都是以native进程形式存在的。上图 /system/bin/surfaceflinger、/system/bin/rild、procrank等就是Native进程。

    Java进程:实例化了Dalvik虚拟机实例的Linux进程,进程的入口main函数为Java函数。Dalvik虚拟机实例的宿主进程是fork()系统调用创建的Linux进程,所以每一个Android上的Java进程实际上就是一个Linux进程,只是进程中多了一个Dalvik虚拟机实例。因此,Java进程的内存分配比Native进程复杂。下图,Android系统中的应用程序基本都是Java进程,如桌面、电话、联系人、状态栏等等。

    Android中进程的堆内存

    进程空间中的Heap空间是我们需要重点关注的。Heap空间完全由程序员控制,我们使用的C Malloc、C++ new和Java new所申请的空间都是Heap空间, C/C++申请的内存空间在Native Heap中,而Java申请的内存空间则在Dalvik Heap中。

    Android的 Java程序为什么容易出现OOM

    这个是因为Android系统对Dalvik的VM Heapsize作了硬性限制,当java进程申请的Java空间超过阈值时,就会抛出OOM异常(这个阈值可以是48M、24M、16M等,视机型而定),可以通过adb shell getprop | grep dalvik.vm.heapgrowthlimit查看此值。

    也就是说,程序发生OMM并不表示RAM不足,而是因为程序申请的Java Heap对象超过了Dalvik VM Heap Growth Limit。也就是说,在RAM充足的情况下,也可能发生OOM。

    这样的设计似乎有些不合理,但是Google为什么这样做呢?这样设计的目的是为了让Android系统能同时让比较多的进程常驻内存,这样程序启动时就不用每次都重新加载到内存,能够给用户更快的响应。迫使每个应用程序使用较小的内存,移动设备非常有限的RAM就能使比较多的App常驻其中。但是有一些大型应用程序是无法忍受VM Heap Growth Limit的限制的。

    Android如何应对RAM不足

    Java程序发生OMM并不是表示RAM不足,如果RAM真的不足,会发生什么呢?这时Android的Memory Killer会起作用,当RAM所剩不多时,Memory Killer会杀死一些优先级比较低的进程来释放物理内存,让高优先级程序得到更多的内存。

    应用程序如何绕过DalvikVM Heap Size的限制

    对于一些大型的应用程序(比如游戏),内存使用会比较多,很容易超超出VM Heapsize的限制,这时怎么保证程序不会因为OOM而崩溃呢?

    创建子进程

    创建一个新的进程,那么我们就可以把一些对象分配到新进程的Heap上了,从而达到一个应用程序使用更多的内存的目的,当然,创建子进程会增加系统开销,而且并不是所有应用程序都适合这样做,视需求而定。

    创建子进程的方法:使用android:process标签

    使用JNI在Native Heap上申请空间(推荐使用)

    Native Heap的增长并不受Dalvik VM heapsize的限制,它的Native Heap Size已经远远超过了Dalvik Heap Size的限制。

    只要RAM有剩余空间,程序员可以一直在Native Heap上申请空间,当然如果RAM快耗尽,Memory Killer会杀进程释放RAM。大家使用一些软件时,有时候会闪退,就可能是软件在Native层申请了比较多的内存导致的。比如,我就碰到过UC Web在浏览内容比较多的网页时闪退,原因就是其Native Heap增长到比较大的值,占用了大量的RAM,被Memory Killer杀掉了。

    使用显存(操作系统预留RAM的一部分作为显存)

    使用OpenGL Textures等API,Texture Memory不受Dalvik VM Heapsize限制。再比如Android中的GraphicBufferAllocator申请的内存就是显存。

    Bitmap分配在Native Heap还是Dalvik Heap上?

    一种流行的观点是这样的:

    Bitmap是JNI层创建的,所以它应该是分配到Native Heap上,并且为了解释Bitmap容易导致OOM,提出了这样的观点:native heap size + dalvik heapsize <= dalvik vm heapsize。但是Native Heap Size远远超过Dalvik VM Heap Size,所以,事实证明以上观点是不正确的。

    正确的观点:

    大家都知道,过多地创建Bitmap会导致OOM异常,且Native Heap Size不受Dalvik限制,所以可以得出结论:

    Bitmap只能是分配在Dalvik Heap上的,因为只有这样才能解释Bitmap容易导致OOM。

    Android内存管理机制

    从操作系统的角度来说,内存就是一块数据存储区域,是可被操作系统调度的资源。在多任务(进程)的OS中,内存管理尤为重要,OS需要为每一个进程合理的分配内存资源。所以可以从OS对内存和回收两方面来理解内存管理机制。

    • 分配机制:为每一个任务(进程)分配一个合理大小的内存块,保证每一个进程能够正常的运行,同时确保进程不会占用太多的内存。
    • 回收机制:当系统内存不足的时候,需要有一个合理的回收再分配机制,以保证新的进程可以正常运行。回收时杀死那些正在占用内存的进程,OS需要提供一个合理的杀死进程机制。

    同样作为一个多任务的操作系统,Android系统对内存管理有有一套自己的方法,手机上的内存资源比PC更少,需要更加谨慎的管理内存。理解Android的内存分配机制有助于我们写出更高效的代码,提高应用的性能。

    下面分别从**分配 回收 **两方面来描述Android的内存管理机制:

    分配机制

    Android为每个进程分配内存时,采用弹性的分配方式,即刚开始并不会给应用分配很多的内存,而是给每一个进程分配一个“够用”的内存大小。这个大小值是根据每一个设备的实际的物理内存大小来决定的。随着应用的运行和使用,Android会为进程分配一些额外的内存大小。但是分配的大小是有限度的,系统不可能为每一个应用分配无限大小的内存。

    总之,Android系统需要最大限度的让更多的进程存活在内存中,以保证用户再次打开应用时减少应用的启动时间,提高用户体验。

    回收机制

    Android对内存的使用方式是“尽最大限度的使用”,只有当内存水足的时候,才会杀死其它进程来回收足够的内存。但Android系统否可能随便的杀死一个进程,它也有一个机制杀死进程来回收内存。

    Android杀死进程有两个参考条件:

    1. 回收收益

    当Android系统开始杀死LRU缓存中的进程时,系统会判断每个进程杀死后带来的回收收益。因为Android总是倾向于杀死一个能回收更多内存的进程,从而可以杀死更少的进程,来获取更多的内存。杀死的进程越少,对用户体验的影响就越小。

    2. 进程优先级

    下面将从 Application Framework 和 Linux kernel 两个层次分析 Android 操作系统的资源管理机制。

    Android 之所以采用特殊的资源管理机制,原因在于其设计之初就是面向移动终端,所有可用的内存仅限于系统 RAM,必须针对这种限制设计相应的优化方案。当 Android 应用程序退出时,并不清理其所占用的内存,Linux 内核进程也相应的继续存在,所谓“退出但不关闭”。从而使得用户调用程序时能够在第一时间得到响应。当系统内存不足时,系统将激活内存回收过程。为了不因内存回收影响用户体验(如杀死当前的活动进程),Android 基于进程中运行的组件及其状态规定了默认的五个回收优先级:

    Android为每一个进程分配了优先组的概念,优先组越低的进程,被杀死的概率就越大。根据进程的重要性,划分为5级:

    1)前台进程(Foreground process)

    用户当前操作所必需的进程。通常在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。

    2)可见进程(Visible process)

    没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

    3)服务进程(Service process)

    尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

    4)后台进程(Background process)

    后台进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU 列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。

    5)空进程(Empty process)

    不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。

    通常,前面三种进程不会被杀死。

    ActivityManagerService 集中管理所有进程的内存资源分配。所有进程需要申请或释放内存之前必须调用 ActivityManagerService 对象,获得其“许可”之后才能进行下一步操作,或者 ActivityManagerService 将直接“代劳”。ActivityManagerService类中涉及到内存回收的几个重要的成员方法如下:trimApplications()、updateOomAdjLocked()、activityIdleInternal() 。这几个成员方法主要负责 Android 默认的内存回收机制,若 Linux 内核中的内存回收机制没有被禁用,则跳过默认回收。

    默认回收过程

    Android 操作系统中的内存回收可分为两个层次,即默认内存回收与Linux内核级内存回收,所有代码可参见 ActivityManagerService.java。

    回收动作入口:activityIdleInternal()

    Android 系统中内存回收的触发点大致可分为三种情况。第一,用户程序调用 StartActivity(), 使当前活动的 Activity 被覆盖;第二,用户按 back 键,退出当前应用程序;第三,启动一个新的应用程序。这些能够触发内存回收的事件最终调用的函数接口就是 activityIdleInternal()。当 ActivityManagerService 接收到异步消息 IDLE_TIMEOUT_MSG 或者 IDLE_NOW_MSG 时,activityIdleInternal() 将会被调用。



    作者:Mr槑
    链接:https://www.jianshu.com/p/2b11639905ec
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    展开全文
  • 1.每个进程的4G内存空间只是虚拟内存空间,每次访问内存空间的某个地址,都需要把地址翻译为实际物理内存地址 2.所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。 3....

    原因:实现内存隔离,进程A的虚拟地址和进程B的虚拟地址不同,这样就防止了进程A将数据信息写入进程B

    1.         每个进程的4G内存空间只是虚拟内存空间,每次访问内存空间的某个地址,都需要把地址翻译为实际物理内存地址

    2.         所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。

    3.         进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,需要用页表来记录

    4.         页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)

    5.         当进程访问某个虚拟地址,去看页表,如果发现对应的数据不在物理内存中,则缺页异常

    6.         缺页异常的处理过程,就是把进程需要的数据从磁盘上拷贝到物理内存中,如果内存已经满了,没有空地方了,那就找一个页覆盖,当然如果被覆盖的页曾经被修改过,需要将此页写回磁盘

     

    每个android进程拥有1g内核空间,3g用户空间,大小参数可配置,内核空间共享

    Binder借助了内存映射的方法,在内核空间和接收方用户空间的数据缓存区之间做了一层内存映射,就相当于直接拷贝到了接收方用户空间的数据缓存区,从而减少了一次数据拷贝

     

    参考:https://mubu.com/doc/explore/21079

    https://www.cnblogs.com/dyllove98/archive/2013/06/12/3132940.html

     

    展开全文
  • Android应用虚拟内存泄漏问题分析 android系统上,各种订制修改比较多,经常会遇到一些奇怪的内存泄漏问题。最近遇到一个比较少见的应用端泄漏,这边记录一下。 首先,应用运行大概2小时左右,遇到如下崩溃: 02...

    android系统上,各种订制修改比较多,经常会遇到一些奇怪的内存泄漏问题。最近遇到一个比较少见的应用端泄漏,这边记录一下。

    最开始,就是使用Android Studio自带的利器:Profiler

     

     

     

    应用运行大概2小时左右,抓取到的图如上,没发现有明显的内存增加,但遇到如下崩溃:

    02-04 13:15:03.661  1070  1087 W libc    : pthread_create failed: couldn't allocate 1069056-bytes mapped space: Out of memory
    02-04 13:15:03.661  1070  1087 W art     : Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again"
    
    E mali_so : encounter the first mali_error : 0x0002 : failed to allocate CPU memory (gles_texturep_upload_3d_internal at hardware/arm/maliT760/driver/product/gles/src/texture/mali_gles_texture_upload.c:1030)
    02-04 13:15:18.664  1070  1627 E OpenGLRenderer: GL error:  Out of memory!
    02-04 13:15:18.665  1070  1627 F OpenGLRenderer: GL errors! frameworks/base/libs/hwui/renderthread/CanvasContext.cpp:550

    不到1M的内存都分配不出来了?通过下面命令,看起来系统还是有500多M空闲内存。

    cat /proc/meminfo
    
    MemTotal:        2045160 kB
    MemFree:          529064 kB
    MemAvailable:    1250020 kB
    Buffers:            1300 kB
    Cached:           891916 kB
    SwapCached:            0 kB
    Active:           556296 kB
    Inactive:         674200 kB
    Active(anon):     235800 kB
    Inactive(anon):   224668 kB
    Active(file):     320496 kB
    Inactive(file):   449532 kB
    Unevictable:         256 kB
    Mlocked:             256 kB

    那就比较奇怪了,这边写了2个脚本,一个是自动点击测试操作,另一个是记录测试时的系统、应用内存以及相关句柄的使用情况。

    测试脚本:

    #!/system/bin/sh
    COUNT=1
    FILE=/sdcard/top.txt
    #input tap 800 500
    #input text 1599828
    #input tap 1600 500
    echo "Auto test start:"
    PROCESS=$1
    if [ "$1" == "" ];then
     echo "./auto_test.sh should with procecess name"
     exit 
    fi
    PID=`pidof $PROCESS`
    echo "start test $PROCESS,pid is $PID:"
    echo "======================================================="
    
    while(true)
    do
    echo $COUNT : `date`
    # input tap 1800 900 
    # input tap 800 500
    #input text 1599828
     #input tap 1600 500
     #input keyevent 66
     
     #swich to face login
     #input tap 800 330
    
    
    procrank | grep -E "$PROCESS|RAM"
    cat /proc/meminfo  | grep -A 2  MemTotal:
    echo "------------------------------------------------------"
    sleep 2
    input tap 1000 700
    sleep 6
    input tap 1900 80
    sleep 2
    #confirm button ok
    input tap 1150 750
    
    if [ ! -d "/proc/$PID" ];then
    	
    	TIME=$(date "+%Y%m%d_%H%M%S")
    	BUG_REPORT=/sdcard/bugreport_${TIME}_${PID}.txt
    	echo "$PROCESS is died at:" `date`
    	echo "save bugreport to:$BUG_REPORT"
    	bugreport > $BUG_REPORT
    	exit
    fi
     COUNT=$(expr $COUNT + 1 )
    done;

    记录脚本中的一部分如下:

    #!/system/bin/sh
    INTERVAL=60
    ENABLE_LOG=true
    PID=$1
    TIME=$(date "+%Y%m%d-%H%M")
    WORK_DIR=/sdcard/rk/$TIME
    MEMINFO_DIR=$WORK_DIR/meminfo
    LSOF_DIR=$WORK_DIR/lsof
    LOG_DIR=$WORK_DIR/log
    SYSTEM_MEM_DIR=$WORK_DIR/system_mem
    STATUS_DIR=$WORK_DIR/status
    THREAD_DIR=$WORK_DIR/thread
    PROCRANK_DIR=$WORK_DIR/procrank
    FD_DIR=$WORK_DIR/fd
    
    PS=$WORK_DIR/ps.txt
    LSOF=$WORK_DIR/lsof.txt
    INFO=$WORK_DIR/info.txt
    
    
    COUNT=1
    
    
    mkdir -p $WORK_DIR
    mkdir -p $MEMINFO_DIR
    mkdir -p $LSOF_DIR
    mkdir -p $LOG_DIR
    mkdir -p $SYSTEM_MEM_DIR
    mkdir -p $STATUS_DIR
    mkdir -p $THREAD_DIR
    mkdir -p $PROCRANK_DIR
    mkdir -p $FD_DIR
    
     #echo `date >> $LOG`
     #echo `date >> $SLAB`
     PROCESS_NAME=`cat /proc/$1/cmdline`
    
    
    #set -x
    if [  $1 ]; then
      echo "================================================"
      echo "Track process: $PROCESS_NAME,pid: $1 "
      echo "Start at : `date`"
      PID_EXIST=` ps | grep -w $PID | wc -l`
    	if [ $PID_EXIST -lt 1 ];then
    			echo "Pid :$1 not exsit!"
    		exit 1
    	fi
    	
    	if [ ! -r  /proc/$PID/fd ];then
    			echo "You should run in root user."
    		exit 2
    	fi
    else
      echo "You should run with track process pid!($0 pid)"
      exit 4
    fi 
     echo "Update logcat buffer size to 2M."
     logcat -G 2M
    echo "Save record in: $WORK_DIR"
    echo Record start at:`date` >> $INFO
    echo "$PROCESS_NAME,pid is:$1" >> $INFO
    echo  -------------------------------------------------->> $INFO
    
    echo "Current system info:" >> $INFO
    echo /proc/sys/kernel/threads-max: >> $INFO
    cat /proc/sys/kernel/threads-max >> $INFO
    echo /proc/$1/limits: >> $INFO
    cat /proc/$1/limits >> $INFO
    
    while((1));do
      NOW=`date`
      
      if [ ! -d "/proc/$PID" ];then
    	echo "$PROCESS_NAME is died,exit proc info record!"
    	echo  -------------------------------------------------->> $INFO
    	echo "Record total $COUNT times." >> $INFO
    	logcat -d >> $LOG_DIR/last_log.txt
    	cp -rf /data/tombstones $WORK_DIR/
    	TIME=$(date "+%Y%m%d_%H%M%S")
    	BUG_REPORT=$WORK_DIR/bugreport_${TIME}_${PID}.txt
    	echo "save bugreport to:$BUG_REPORT"
    	bugreport > $BUG_REPORT
    	exit
      fi
      
      NUM=`ls -l /proc/$PID/fd | wc -l`
      TIME_LABEL="\n$NOW:--------------------------------------------------:$COUNT"
      
      echo -e  $TIME_LABEL >> $PS
      `ps >> $PS`
      
    
    
    
      
      echo -e  $TIME_LABEL >> $MEMINFO_DIR/${COUNT}_meminfo.txt
      dumpsys meminfo $1 >> $MEMINFO_DIR/${COUNT}_meminfo.txt
      
      echo -e  $TIME_LABEL >> $SYSTEM_MEM_DIR/${COUNT}_sys_meminfo.txt
      cat /proc/meminfo >> $SYSTEM_MEM_DIR/${COUNT}_sys_meminfo.txt
        
      
      echo -e  $TIME_LABEL >> $PROCRANK_DIR/${COUNT}_procrank.txt
      procrank >> $PROCRANK_DIR/${COUNT}_procrank.txt
        
    
      COUNT=$(expr $COUNT + 1 )
      sleep $INTERVAL
    done

    测试大概2小时左右,问题复现。对比上面脚本记录下来的信息,未发现有句柄泄漏,cat /proc/meminfo显示系统可用内存并未明显减少。在对比ps的不同时间段结果时,有发现:

    u0_a9     1070  217   2057984 379264 SyS_epoll_ b5fd37a4 S com.cnsg.card
    u0_a9     1070  217   2663308 581404 binder_thr b5fd38e8 S com.cnsg.card

    通过procrank抓取的对比,发现一段时间后,发现:

     上面1070进程的Vss部分持续稳定地在增长。搜索的了相关资料:

    VmRSS不能判定内存泄漏, VmSize才可以

    典型的内存泄漏往往会使VmSize和VmRSS同时增长,内存泄漏通过观察VmRSS就能发现(也可能发现不了, 比如只malloc不使用);但并不是说VmRSS增长了就有内存泄漏。

    其实对VmSize的监控才是合理的,原因如下:

    VmSize是进程所有的内存(文件映射,共享内存,堆,任何其它的内存的总和,它包含VmRSS),它的变化并不是想象的“非常快”,ch___mgr就长期稳定在58824 K上,因为没有不匹配的malloc/free,VmSize不会涨上去

    VmRSS是实际用到的物理内存,由于业务的需要它增长变化是合理的,就比如此例中的ch___mgr

    打个比方,VmSize是一个官员拥有多少资产(固定资产、存银行的钱、放家里的钱、等等所有资产的总和)

    VmRSS 是这官员人放在家里的钱

    我们现在监控这个官员有没有贪污钱财,应该监控他所有的资产VmSize有没有增加,而不是监控放在家里的钱VmRSS

    如果官员从银行里面把钱取出来放在家里就认定为贪污,显然很荒唐

     

    可以确认,出现了虚拟内存泄漏。那下一步,就是确认这个进程,是哪个模块导致虚拟内存泄漏了。我们没有这个应用的代码,分析一时陷入死角。那继续从/proc/1070/中寻找突破中。

    /proc/1070/status中,会记录进程的许多状态,如下:

    ***/proc/1070 # cat status        
    Name:   com.cnsg.card                    
    State:  S (sleeping)                       
    Tgid:   1070                              
    Ngid:   0                                  
    Pid:    1070                              
    PPid:   343                                
    TracerPid:      0                          
    Uid:    10062   10062   10062   10062      
    Gid:    10062   10062   10062   10062      
    FDSize: 128                                
    Groups: 3003 9997 50062                    
    VmPeak:  3619748 kB                        
    VmSize:  3531764 kB                        
    VmLck:         0 kB                        
    VmPin:         0 kB                        
    VmHWM:    878692 kB                        
    VmRSS:    487580 kB                        
    VmData:  1317136 kB                        
    VmStk:      8192 kB                        
    VmExe:        16 kB                        
    VmLib:    177524 kB                        
    VmPTE:      3488 kB                        
    VmPMD:        32 kB                        
    VmSwap:        0 kB                        
    Threads:        48                         
    SigQ:   0/15299                            
    SigPnd: 0000000000000000                   
    ShdPnd: 0000000000000000                   
    SigBlk: 0000000000001204                   
    SigIgn: 0000000000000000                   
    SigCgt: 20000002000084f8                   
    CapInh: 0000000000000000                   
    CapPrm: 0000000000000000                   
    CapEff: 0000000000000000                   
    CapBnd: 0000000000000000                   
    CapAmb: 0000000000000000                   
    Seccomp:        0                          
    Cpus_allowed:   30                         
    Cpus_allowed_list:      4-5                
    Mems_allowed:   1                          
    Mems_allowed_list:      0                  
    voluntary_ctxt_switches:        619990     
    nonvoluntary_ctxt_switches:     88065      

    上面的相关解释:

    用户进程在/proc/{pid}/status文件中记录了该进程的内存使用实时情况。
        * VmSize:
          虚拟内存大小。
          整个进程使用虚拟内存大小,是VmLib, VmExe, VmData, 和 VmStk的总和。
        * VmLck:
          虚拟内存锁。
          进程当前使用的并且加锁的虚拟内存总数
        * VmRSS:
          虚拟内存驻留集合大小。
          这是驻留在物理内存的一部分。它没有交换到硬盘。它包括代码,数据和栈。
        * VmData:
          虚拟内存数据。
          堆使用的虚拟内存。
        * VmStk:
          虚拟内存栈
          栈使用的虚拟内存
        * VmExe:
          可执行的虚拟内存
          可执行的和静态链接库所使用的虚拟内存
        * VmLib:
          虚拟内存库
          动态链接库所使用的虚拟内存

    那就继续跑脚本,同时把这个进程的这个节点,也加入观察。运行一段时间后,发现除了Vm相关的变化,Threads也在持续增加,而且暂停测试,Threads也不会减少,好家伙,看来进程有持续创建进程,但没有销毁。进一步的,想知道是什么线程被持续创建呢,这边用到:pstree命令

    busybox pstree 1070
    com.cnsg.card-+-{Binder:15994_1}
                    |-{Binder:15994_2}
                    |-{Binder:15994_3}
                    |-{Binder:15994_4}
                    |-{Binder:15994_5}
                    |-{FinalizerDaemon}
                    |-{FinalizerWatchd}
                    |-{HeapTaskDaemon}
                    |-{JDWP}
                    |-{Jit thread pool}
                    |-{Profile Saver}
                    |-{ReferenceQueueD}
                    |-{RenderThread}
                    |-{Signal Catcher}
                    |-{Thread-11}
                    |-{Thread-12}
                    |-{Thread-15}
                    |-2*[{Thread-2}]
                    |-{Thread-4}
                    |-{Thread-5}
                    |-{Thread-6}
                    |-{Thread-7}
                    |-{Thread-8}
                    |-{Thread-9}
                    |-{hwuiTask1}
                    |-{hwuiTask2}
                    |-{mali-cmar-backe}
                    |-{mali-hist-dump}
                    |-{mali-mem-purge}
                    |-{RxCachedThreadS}(1927)
                    |-{RxCachedThreadS}(1928)
                    |-{RxCachedThreadS}(2140)
                    |-{RxCachedThreadS}(2141)
                    |-{RxCachedThreadS}(2289)
                    |-{RxCachedThreadS}(2290)
                    |-{RxCachedThreadS}(2458)
                    |-{RxCachedThreadS}(2464)
                    |-{RxCachedThreadS}(2465)
                    |-{RxCachedThreadS}(2614)
                    |-{RxCachedThreadS}(2615)
                    |-{RxCachedThreadS}(2792)
                    |-{RxCachedThreadS}(2793)
                    |-{RxCachedThreadS}(2958)
                    |-6*[{mali-utility-wo}]
                    |-10*[{myHandlerThread}]
                    `-{com.cnsg.card}

    通过不同时段的对比,发现主要是RxCachedThreadS这类型线程在增加。到这里,原因就清楚了,进程中存在持续创建线程的代码,在创建时,都需要申请内存空间,包括虚拟和物理的,这边虚拟内存先被占满了,就出现了最前面log的错误。

    展开全文
  • 在GeForce GTX 750Ti/750显卡上,我们已经见识到了Maxwell架构在高效能上的威力,但Maxwell架构到现在还是个迷,NVIDIA还没有公布详细的架构特性,特别是有关去年GTC大会上公布的路线图中的“统一虚拟内存”...
  • Android 的 /data 目录下添加虚拟内存Android 系统上添加虚拟内存已经不是什么新鲜事了, 很多手机都支持, 通常都是将虚拟内存添加到 SD 卡上, 不过由于 SD 卡的的读写速度有速度限制, 再加上如果不是用高速的...
  • 创建UpCatcher是为了解决Android应用虚拟内存需求。 像“共享首选项”一样使用它,不同之处在于仅当系统内存至关重要时,它将对象转换为json并写入外部存储。 其余时间它将用作HashMap 。 两个简单的功能; ...
  • CPU 指令对内存的要求 对于 CPU 执行指令来说有三种重要的寄存器: PC 寄存器(Program Counter Register:程序计数器),我们也称作指令地址寄存器(Instruction Address Register)。顾名思义,他就是用来存储下...
  • android修改虚拟内存(方法)

    千次阅读 2015-05-22 21:45:07
    如果有理有源码的话, 修改:out/target/product/xxx/system/build...dalvik.vm.heapstartsize=12m --------------->应用启动,立马分屏12M内存 dalvik.vm.heapgrowthlimit=72m --------------->整个应用,
  • 最近刚安装Android studio2.2 版本,创建工程时老是报错 然后上网百度一下,发现是堆栈虚拟内存不足,解决办法是:在gradle.properties文件中加入如下代码:  MaxHeapSize=512m 然后,重新同步一下gradle就行了
  • 2.1.2 Android虚拟设备和模拟器Android虚拟设备是模拟器的一种配置。开发人员通过定义需要的硬件和软件选项来使用Android模拟器模拟真实的设备。一个Android虚拟设备由以下几部分组成:硬件配置:定义虚拟设备的硬件...
  • Android系统手动创建虚拟内存swap

    千次阅读 2019-10-07 10:08:01
    首先要使用的工具是android的sdk中的adb.exe 然后用cmd打开adb shell,这里注意android手机打开调试模式与电脑进行usb连接 输入以下命令: mount -o remount,rw /cache; //在cache目录下生成swap.img文件,具体...
  • 摘要 简述启动过程的内存分配,各个映像的烧写,加载,logo的刷新,文件系统mount。 DRAM:外部RAM;...1概述-分区layout以及虚拟内存布局 一概述 首先了解mt6573nand flash的16分区结构
  • 对手机本地图片或网络图片,进行缓存.虚拟内存等存取实例代码忆调试通过.
  • Swap space for Android GB and ICS Antutu @1.2ghz Stock 232 ICS Webtop with Swap Space @900MBCF Bench @1.2ghz Stock 232 ICS Webtop with Swap Space @900MBAntutu @1.2ghz ...
  • 这里针对上图进行简单描述:1)物理地址与虚拟地址:虚拟内存是程序和物理内存之间引入的中间层,目的是解决直接使用物理内存带来的安全性问题、超过物理内存大小需求无法满足等等问题。而Linux的内存管理就是建立在...
  • 原标题:Android内存优化大盘点本文作者作者:Stan_Zhttps://www.jianshu.com/p/fba7b43bdc9c做内存优化的...老规矩,先上大纲: 1基础知识1.1 Android内存管理框架: 这里针对上图进行简单描述:1)物理地址与虚拟地...
  • Android虚拟机AVD加一个虚拟内存

    千次阅读 2015-07-17 10:50:40
    Android虚拟机AVD加一个虚拟sdcard 在写音乐播放器的时候,不想把每次写好的APK装到手机中来运行,个人感觉挺麻烦的,就想着在我的AVD里面放几首音乐进去,这样测试比较方便,但是通过DDMS的File Explorer往手机...
  • Android进程内存分析

    2020-07-30 11:28:16
    Vss是占用的虚拟内存,如果没有映射实际的内存也算进来。 如果VSS这个概念不好理解,那么参考如下这段: VSS (reported as VSZ from ps) is the total accessible address space of a process.This size also ...
  • 安卓如何增加虚拟内存?一般情况下,系统占用手机运行内存(RAM)太多,导致剩余RAM太少,从而造成设备在运行过程中不够流畅。其实,我们只有通过增加虚拟内存的方法,也就是说,在内存卡(TF卡)中增加一个区域供运行...
  • 转自:http://www.csdn123.com/html/blogs/20131011/81402.htmAndroid对内存的...Android中kernel和应用程序都可能会有内存泄漏和越界。对于Java代码,在越界的时候虚拟机会加以检查并抛出异常。而对于C/C++代码,越...
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼如何增加 手机 虚拟内存?手机的RAM太小了怎么办?怎么样增加手机内存?没关系,只要你的手机有SD卡,下边的方法就能帮你增大手机的运行内存,不过该方法容易对SD卡有损伤,请...
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼如何增加 手机 虚拟内存?手机的RAM太小了怎么办?怎么样增加手机内存?没关系,只要你的手机有SD卡,下边的方法就能帮你增大手机的运行内存,不过该方法容易对SD卡有损伤,请...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,007
精华内容 402
关键字:

android虚拟内存