我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android虚拟机中的内存分配与OOM问题详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android虚拟机中的内存分配与OOM问题详解

背景知识

Android中每个App默认情况下是运行在一个独立进程中的, 而这个独立进程正是从Zygote孵化出来的VM进程, 也就是说, 也就是说每个Android APP在运行时会启动一个Java虚拟机。

并且系统会给它分配固定的内存空间(手机厂商会根据手机的配置情况来对其进行调整)。

一、Android VM的内存空间

Android是一个多任务系统, 为了保证多任务的运行, Android给每个App可使用的Heap大小设定了一个限定值.

这个值是系统设置的prop值, 保存在System/build.prop文件中. 一般国内的手机厂商都会做修改, 根据手机配置不同而不同, 可以直接打开查看与修改。

其中和虚拟机内存相关的主要有以下三个:

1 . dalvik.vm.heapstartsize

– App启动后,系统分配给它的Heap初始大小,随着App使用可增加。

2 . dalvik.vm.heapgrowthlimit

– 默认情况下, App可使用的Heap的最大值, 超过这个值就会产生OOM.

3 . dalvik.vm.heapsize

– 如果App的manifest文件中配置了largeHeap属性, 那么App可使用的Heap的最大值为此项设定值。

 <application
    android:largeHeap="true">
    ...
</application>

所以对于同一个手机,不开启largeHeap属性时与多进程时,每个APP的虚拟机分配的内存的上限都是heapgrowthlimit

1.查看内存的API

Android在ActivityManager类中提供了API可以运行时获取这些属性值,如下:

//ActivityManager的getMemoryClass()获得内用正常情况下内存的大小,即heapgrowthlimit的值
//ActivityManager的getLargeMemoryClass()可以获得开启largeHeap最大的内存大小,即heapsize的指
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryClass();
activityManager.getLargeMemoryClass();

二、Android VM内存分配流程

虚拟机分配内存的具体源码可以AOSP的Heap.cpp文件中查看:


static void *tryMalloc(size_t size)
{
    void *ptr;
//TODO: figure out better heuristics
//    There will be a lot of churn if someone allocates a bunch of
//    big objects in a row, and we hit the frag case each time.
//    A full GC for each.
//    Maybe we grow the heap in bigger leaps
//    Maybe we skip the GC if the size is large and we did one recently
//      (number of allocations ago) (watch for thread effects)
//    DeflateTest allocs a bunch of ~128k buffers w/in 0-5 allocs of each other
//      (or, at least, there are only 0-5 objects swept each time)
    ptr = dvmHeapSourceAlloc(size);
    if (ptr != NULL) {
        return ptr;
    }
    
    if (gDvm.gcHeap->gcRunning) {
        
        dvmWaitForConcurrentGcToComplete();
    } else {
      
      gcForMalloc(false);
    }
    ptr = dvmHeapSourceAlloc(size);
    if (ptr != NULL) {
        return ptr;
    }
    
    ptr = dvmHeapSourceAllocAndGrow(size);
    if (ptr != NULL) {
        size_t newHeapSize;
        newHeapSize = dvmHeapSourceGetIdealFootprint();
//TODO: may want to grow a little bit more so that the amount of free
//      space is equal to the old free space + the utilization slop for
//      the new allocation.
        LOGI_HEAP("Grow heap (frag case) to "
                "%zu.%03zuMB for %zu-byte allocation",
                FRACTIONAL_MB(newHeapSize), size);
        return ptr;
    }
    
//TODO: wait for the finalizers from the previous GC to finish
    LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation",
            size);
    gcForMalloc(true);
    ptr = dvmHeapSourceAllocAndGrow(size);
    if (ptr != NULL) {
        return ptr;
    }
//TODO: maybe wait for finalizers and try one last time
    LOGE_HEAP("Out of memory on a %zd-byte allocation.", size);
//TODO: tell the HeapSource to dump its state
    dvmDumpThread(dvmThreadSelf(), false);
    return NULL;
}

具体流程如下:

  • 尝试分配,如果成功则返回,失败则转入步骤2
  • 判断是否gc正在进行垃圾回收,如果正在进行则等待回收完成之后,尝试分配。如果成功则返回,失败则转入步骤3
  • 自己启动gc进行垃圾回收,这里gcForMalloc的参数是false。所以不会回收软引用,回收完成后尝试分配,如果成功则返回,失败则转入步骤4
  • 调用dvmHeapSourceAllocAndGrow尝试分配,这个函数会扩张堆的大小,失败转入步骤5
  • 进入回收软引用阶段,这里gcForMalloc的参数是ture,所以需要回收软引用。然后再调用dvmHeapSourceAllocAndGrow尝试分配,如果失败则抛出OOM

小结

所以产生OOM时,一定是java的堆中 已有的内存 + 申请的内存 >= heapgrowthlimit导致的,不会因为手机目前物理内存是否紧张而改变 - 当物理内存非常紧张时系统会通过LowMemory Killer杀掉一些低优先级的进程。

相应的,物理内存非常充足的情况也会有OOM的情况发生。

三、出现OOM的建议解决方案

当APP出现OOM时,建议可以从以下两个方向来处理:

1 . 排查内存泄露问题

  • 排查各个功能是否内存泄露情况,可以通过Android Studio中的MemoryMonitor功能进行分析,Memory Monitor也集成了HPROF Viewer和Allocation Tracker可以分析内存快照与内存分配追踪。另外推荐一个工具,square公司开源的leakcanary,非常简洁好用。

  • 排查进程初始化时就直接申请并常驻内存的对象以及其他功能里申请的static对象或者单例对象的必要性。

2 . 内存优化

按照谷歌在youtube上发布的性能优化典范之内存篇,优化各功能的内存,或可参照 胡凯的总结 。

大致有以下这些,具体请参见原文:

  • 谨慎使用large heap
  • 综合考虑设备的内存阈值与其他因素设计合适的缓存大小
  • onLowMemory与onTrimMemory
  • 资源文件需要选择合适的文件夹进行存放
  • Try catch某些大内存分配的操作
  • 谨慎使用static对象
  • 特别留意单例对象中不合理的持有
  • 珍惜Services资源
  • 优化布局层次,减少内存消耗
  • 谨慎使用“抽象”编程
  • 使用nano protobufs序列化数据
  • 谨慎使用依赖注入框架
  • 谨慎使用多进程
  • 使用ProGuard来剔除不需要的代码
  • 谨慎使用第三方libraries

考虑不同的实现方式来优化内存占用

  • 注意Activity的泄露
  • 考虑使用Applicaiton Context代替Activity Context
  • 注意临时Bitmap对象的及时回收
  • 注意监听器的注销
  • 注意缓存容器里的对象泄露
  • 注意Webview的泄露

注意Cursor对象的及时关闭

  • 复用系统自带的资源
  • ListView中对ConvertView的复用
  • Bitmap对象的复用
  • 避免在ondraw方法里执行对象的创建

StringBuilder代替String

  • 使用更加轻量的数据结构
  • 避免在Android里使用enum
  • 减少Bitmap对象的内存占用

使用更小的图片

  • 减少对象的内存占用
  • 内存对象的重复利用
  • 避免对象的内存泄露
  • 内存使用策略的优化

以上就是Android 虚拟机中的内存分配与OOM问题详解的详细内容,更多关于Android虚拟机内存分配OOM的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android虚拟机中的内存分配与OOM问题详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

详解Java 虚拟机(第⑥篇)——内存分配与回收策略

一、Minor GC 和 Full GCMinor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC
2023-06-05

如何解决虚拟机内存不足的问题

解决虚拟机内存不足的问题有几种方法:增加虚拟机内存:可以通过虚拟机管理工具增加虚拟机的内存大小,提高虚拟机的内存容量。优化虚拟机内存使用:可以通过关闭不必要的应用程序或服务,减少虚拟机内存的占用,从而提高虚拟机的内存利用率。使用虚拟内存:虚
如何解决虚拟机内存不足的问题
2024-06-11

Java虚拟机中内存分配与回收策略的示例分析

小编给大家分享一下Java虚拟机中内存分配与回收策略的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!内存分配与回收策略Java技术体系的自动内存管理,最根
2023-06-25

JAVA虚拟机(JVM)详细讲解(二)——内存的划分

我们知道,在C++语言里,如果想使用一个对象,需要对其进行new操作;如果不用这个对象了,需要对其进行delete操作。一旦开发人员忘记写delete语句了,就会造成内存泄露。【内存被对象占用着不还,就叫内存泄露。】而java就聪明了,它从“手动”进化成了“自
JAVA虚拟机(JVM)详细讲解(二)——内存的划分
2020-08-19

Java中的JVM是如何实现内存管理的?(请解释Java虚拟机(JVM)如何进行内存分配与回收?)

Java虚拟机(JVM)通过自动内存管理机制,在Java程序运行时管理内存分配和回收。新创建的对象分配到堆内存的年轻代,称为伊甸园,存活时间增长后晋升到幸存者区域,再晋升到年老代。JVM使用不同的垃圾回收器,如标记-清除、复制和标记-整理,清除垃圾对象。此外,JVM还提供其他内存管理机制,如本地内存、栈内存和方法区,确保程序稳定运行。
Java中的JVM是如何实现内存管理的?(请解释Java虚拟机(JVM)如何进行内存分配与回收?)
2024-04-02

Golang程序中变量的内存分配与存储位置详解

标题:Golang程序中变量的内存分配与存储位置详解在Golang中,变量是程序中存储数据的基本单元。在编写Golang程序时,了解变量的内存分配和存储位置对于优化程序性能和避免内存泄漏非常重要。本文将深入探讨Golang程序中变量的内存
Golang程序中变量的内存分配与存储位置详解
2024-02-28

C++中内存泄漏问题的分析与解决方案

C++中内存泄漏问题的分析与解决方案概述:内存泄漏是指程序在动态分配内存后,没有及时释放导致内存无法再被程序使用的情况。在C++开发中,内存泄漏是一个常见且严重的问题,一旦发生,会导致程序运行效率下降,最终可能导致程序崩溃。本文将对C++中
2023-10-22

windows系统中怎样设置虚拟内存才能解决物理内存较低的问题

windows系统中怎样设置虚拟内存才能解决物理内存较低的问题,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。当我们在运行一些大型的软件,或者是刚刚退出游戏的时候
2023-06-14

如何解决C程序中Ubuntu、stm32的内存分配问题

这篇文章主要介绍了如何解决C程序中Ubuntu、stm32的内存分配问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、内存分区概念介绍1.1、C/C++编译程序的内存占用
2023-06-22

详细解读Cocos2d Android 项目的生成与运行中存在的问题

编程学习网:对于Cocos2D-Android,估计很多同学都不甚了解,本篇教程将带你详细解读Cocos2D-Android项目的生成与运行中存在的问题。
详细解读Cocos2d Android 项目的生成与运行中存在的问题
2024-04-23

C++ 函数调试详解:如何调试包含动态内存分配的函数中的问题?

在 c++++ 中调试包含动态内存分配的函数时,可使用:调试器(gdb/lldb)检查内存分配/释放(valgrind)断言异常处理实战案例:函数 free_twice 错误:释放已释放内存使用 gdb 调试,发现断言失败检查变量值,确定问
C++ 函数调试详解:如何调试包含动态内存分配的函数中的问题?
2024-05-04

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录