浅谈 maxMemory , totalMemory , freeMemory 和 OOM 与 native Heap

简介: 作者:林冠宏 / 指尖下的幽灵掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8博客:http://www.cnblogs.com/linguanh/GitHub : https://github.com/af913337456/腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities回答内存管理类面试问题可以说出下面这些内容,加分。

作者:林冠宏 / 指尖下的幽灵

掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8

博客:http://www.cnblogs.com/linguanh/

GitHub : https://github.com/af913337456/

腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities

回答内存管理类面试问题可以说出下面这些内容,加分。


前言: 站在巨人的肩膀上,总结此文。

目录:

  • Java runtime 三个计算内存函数
  • OOM 的说法,为什么大型游戏能申请那么多内存?
  • 如何绕过dalvikvm heap size的限制 ?
  • Bitmap分配在native heap还是dalvik heap上?

1,Java runtime 三个计算内存函数:

maxMemory
获取当前 APP 最大能够申请的内存,在 Java Heap 部分。

totalMemory
获取当前 APP 已经从系统拿到的内存,包含使用上了的和没有用上的,因为一般申请会申请多一部分,它总是慢慢按需要从系统拿取。

freeMemory
获取当前 APP 拿到的内存中,还没用上的,即是可以被 gc 回收的。

计算此刻 APP 在 Java Heap 层次已经使用了的内存 usedMemory :


usedMemory = totalMemory - freeMemory

2,OOM 的说法,为什么大型游戏能申请那么多内存?

在不同的 Android 系统版本中,OOM 的判断是不一样的。

  • 通俗来说,OOM 是当前进程共申请的内存综和超过一个限制,而被抛出。

  • 专业来说,Android为每个进程设置Dalvik Heap Size阈值,这个阈值在不同的设备上会因为RAM大小不同而各有差异。如果APP想要分配的内存超过这个阈值,就会发生OOM。

  • Android 3.x以前,Bitmap分配在Native heap中,而在3.x之后,Bitmap分配在Dalvik或ART的Java heap中。

  • Android 2.x系统,当dalvik allocated + native allocated + 新分配的大小 >= dalvik heap 最大值时候就会发生OOM,也就是说在2.x系统中,考虑native heap对每个进程的内存限制。

  • Android 3.x系统,废除了native的计数器,类似bitmap的分配改到dalvik的java heap中申请,只要allocated + 新分配的内存 >= dalvik heap 最大值的时候就会发生OOM(art运行环境的统计规则还是和dalvik保持一致),也就是说在3.x系统中,不考虑native heap对每个进程的内存限制,native heap只会收到本机总内存(包括RAM以及SWAP区或分页文件)的限制。

这也是为什么有些APP(比如大型游戏)可以超过 Dalvik Heap Size 这个值?那是因为Java内存又分为Java Heap和Native Heap,3.X 后 Native Heap是不受该值约束的。像C/C++的内存都是在Native Heap中分配的。另外Bitmap是在Java Heap中分配的,我们开发过程中经常遇到由Bitmap引起的OOM,这就是一个例子。

3,如何绕过dalvikvm heap size的限制 ?

  • 创建子进程,上面说了,内存分配按进程来。再使用进程通讯

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

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

  • 按不同的系统版本,使用 jni 在native heap上申请空间(推荐使用)

3.X 后的系统 native heap的增长并不受dalvik vm heapsize的限制,只要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申请的内存就是显存。

4,Bitmap分配在native heap还是dalvik heap上?

上面说了,不同的系统版本不同,那么在 3.X 及其之后,为什么在 java heap 而不是在 native heap 。请看下面源码。

主要的文件

framework/base/graphic/java/Android/graphics/BitmapFactory.java  
framework/base/core/jni/Android/graphics/BitmapFactory.cpp  
framework/base/core/jni/Android/graphics/Graphics.cpp  

BitmapFactory.java 里面有几个decode***方法用来创建bitmap,最终都会调用:


private staticnative Bitmap nativeDecodeStream(InputStream is, byte[] storage,Rect padding,Options opts);

而nativeDecodeStream()会调用到BitmapFactory.cpp中的deDecode方法,最终会调用到Graphics.cpp的createBitmap方法。

createBitmap方法的实现:

jobjectGraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,  
                                  boolisMutable, jbyteArray ninepatch, int density)  
{  
    SkASSERT(bitmap);  
    SkASSERT(bitmap->pixelRef());  
   
    jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,  
           static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)),  
            buffer, isMutable, ninepatch,density);  
    hasException(env); // For the side effectof logging.  
    return obj;  
}  

从代码中可以看到bitmap对象是通过env->NewOject(...)创建的,到这里疑惑就解开了,bitmap对象是虚拟机创建的,JNIEnv的NewOject方法返回的是java对象,并不是native对象,所以它会分配到dalvik heap中。

如果您认为这篇文章还不错或者有所收获,您可以通过扫描一下下面的支付宝二维码 打赏我一杯咖啡【物质支持】,也可以点击右下角的【推荐】按钮【精神支持】,因为这两种支持都是我继续写作,分享的最大动力

、
img_12e3f54d4d0f70f0eb14f20548e3d781.png
目录
相关文章
|
存储 缓存 并行计算
CPU组成元素:运算器+控制器(一)
CPU组成元素:运算器+控制器
5567 0
|
SQL 数据库 数据库管理
PowerDesigner16:导入SQL脚本、显示中文注释
PowerDesigner16:导入SQL脚本、显示中文注释
PowerDesigner16:导入SQL脚本、显示中文注释
|
7月前
|
存储 自然语言处理 算法
大模型应用:基于千问大模型 + 图算法:可追溯知识图谱问答系统构建.83
本文介绍“图算法+大模型”双引擎智能问答方案:用PageRank、最短路径等图算法处理关系推理与知识验证,由千问大模型负责自然语言理解与生成,实现可信、可溯源、强推理的高可靠问答,破解幻觉、推理弱、不可溯三大难题。
804 7
|
9月前
|
机器学习/深度学习 人工智能 算法
自进化≠自我刷题,Agent 真正的突破口是「自己造环境」?——6 位领域学者 · 7 大议题 · 2 万字圆桌观点实录
AI AMA首期聚焦“Agent自进化”,由魔搭等联合发起,邀请翟云鹏主持及5位顶会作者深度对谈。围绕定义、泛化能力、反馈信号、评估体系等7大议题,探讨Agent如何突破“从零开始”瓶颈,实现类人经验积累与策略优化。(239字)
835 6
|
10月前
|
消息中间件 缓存 NoSQL
开源上门预约系统源码
本文深度解析开源上门预约系统核心设计:涵盖时间冲突校验、人员排班、订单状态流转、多角色协同及消息通知等关键模块,结合Spring Boot、Redis、RabbitMQ等主流技术,提供可落地的代码实现与架构实践。(239字)
|
SQL 分布式计算 Apache
Dataphin x Iceberg 开箱即用的数据湖治理解决方案
Apache Iceberg作为新一代开源数据湖表格式,具备ACID事务、时间旅行和高效Schema演化等能力。Dataphin已完成与Iceberg的深度集成,通过全链路适配与性能优化,为企业提供开箱即用的数据湖治理方案,涵盖数据源支持、离线与实时数据集成、数据研发等核心模块,助力构建现代化数据架构。
659 0
|
缓存 算法 安全
被追着问UUID和自增ID做主键哪个好,为什么?
讨论了UUID和自增ID作为数据库主键的优缺点。UUID全局唯一,适合分布式系统,但存储空间大,不适合范围查询。自增ID存储空间节省,查询效率高,但分库分表困难,可预测性高。UUID版本包括基于时间戳(V1)、随机数(V4)以及基于名称空间的MD5(V3)和SHA1(V5)散列。
2042 1
被追着问UUID和自增ID做主键哪个好,为什么?
|
运维 安全 Linux
IDC服务器故障排除思路
本文详细介绍了服务器维修流程,包括维修前的工具和备件准备,以及不拆机情况下的初步检查步骤。文中还提供了拆机维修的具体方法,如最小化测试法、替换法和交叉比较法,并针对CPU、主板、内存、硬盘、电源、风扇、网卡及BMC等主要配件的故障排除进行了说明,强调了注意事项,旨在帮助技术人员快速准确地定位并解决问题。
1582 14
|
Oracle 关系型数据库 数据库
实时计算 Flink版操作报错合集之执行Flink job,报错“Could not execute SQL statement. Reason:org.apache.flink.table.api.ValidationException: One or more required options are missing”,该怎么办
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
1446 0
|
NoSQL Java MongoDB
Spring Boot中的@Document注解:原理与使用
Spring Boot中的@Document注解:原理与使用