浅谈 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.cppcreateBitmap方法。

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
目录
相关文章
|
Shell API Android开发
android queries属性
android queries属性
965 2
|
存储 算法
halcon模板匹配实践(1)算子参数说明与算子简介
halcon模板匹配实践(1)算子参数说明与算子简介
1106 0
|
机器学习/深度学习 缓存 监控
Pytorch学习笔记(7):优化器、学习率及调整策略、动量
Pytorch学习笔记(7):优化器、学习率及调整策略、动量
1814 0
Pytorch学习笔记(7):优化器、学习率及调整策略、动量
|
5月前
|
缓存 安全 API
RESTful与GraphQL:电商API接口设计的技术细节与适用场景
本文对比了RESTful与GraphQL这两种主流电商API接口设计方案。RESTful通过资源与HTTP方法定义操作,简单直观但可能引发过度或欠获取数据问题;GraphQL允许客户端精确指定所需字段,提高灵活性和传输效率,但面临深度查询攻击等安全挑战。从性能、灵活性、安全性及适用场景多维度分析,RESTful适合资源导向场景,GraphQL则适用于复杂数据需求。实际开发中需根据业务特点选择合适方案,或结合两者优势,以优化用户体验与系统性能。
|
12月前
|
SQL 安全 网络安全
网络安全的护城河:漏洞防御与加密技术的深度解析
【10月更文挑战第37天】在数字时代的浪潮中,网络安全成为守护个人隐私与企业资产的坚固堡垒。本文将深入探讨网络安全的两大核心要素——安全漏洞和加密技术,以及如何通过提升安全意识来强化这道防线。文章旨在揭示网络攻防战的复杂性,并引导读者构建更为稳固的安全体系。
336 1
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的医院门诊预约挂号系统
基于Java+Springboot+Vue开发的医院门诊预约挂号系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的门诊预约挂号管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
403 2
基于Java+Springboot+Vue开发的医院门诊预约挂号系统
|
存储 开发框架 数据可视化
深入解析Android应用开发中的四大核心组件
本文将探讨Android开发中的四大核心组件——Activity、Service、BroadcastReceiver和ContentProvider。我们将深入了解每个组件的定义、作用、使用方法及它们之间的交互方式,以帮助开发者更好地理解和应用这些组件,提升Android应用开发的能力和效率。
1073 5
|
机器学习/深度学习 自动驾驶 算法
深度学习之旋转包围盒检测
旋转包围盒检测是一种高级目标检测方法,旨在识别图像中目标的精确位置和方向。与传统的轴对齐矩形框(水平包围盒)不同,旋转包围盒(Rotated Bounding Box, RBB)允许检测框随目标旋转,从而更紧密地包围目标,尤其适用于长条形、倾斜或旋转的物体。深度学习在旋转包围盒检测中展现了强大的能力,通过训练神经网络模型,能够有效检测和回归旋转包围盒。
224 2
|
存储 前端开发 安全
《Solidity 简易速速上手小册》第9章:DApp 开发与 Solidity 集成(2024 最新版)(上)
《Solidity 简易速速上手小册》第9章:DApp 开发与 Solidity 集成(2024 最新版)
242 0