浅谈 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
回答内存管理类面试问题可以说出下面这些内容,加分。
前言: 站在巨人的肩膀上,总结此文。
目录:
- 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
中。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux中awk的使用方法详解
- Django上线部署之Apache的方法
- centos7系统下python2与python3共存
- linux把一个文件的内容复制到另一个文件的末尾
- 详解linux下nohup日志输出过大问题解决方案
- K8S dashboard 2.0 安装配置并使用 ingress-nginx 访问
- linux模糊查找文件用什么命令比较好
- Ubuntu14.04服务器环境下配置PHP7.0+Apache2+Mysql5.7的方法
- Linux C 后台服务程序单进程控制的实现
- Linux下SSH免密码登录配置详解
- SSH端口转发实现内网穿透的实现
- 在linux中用同一个版本的R 同时安装 Seurat2 和 Seurat3的教程
- AUCell:在单细胞转录组中识别细胞对“基因集”的响应
- linux查看硬件配置命令的方法示例
- Ubuntu环境编译安装PHP和Nginx的方法