Android 升级到 Lollipop 后 NDK mmap 调用在 32 位设备上中断

Android NDK mmap call broken on 32-bit devices after upgrading to Lollipop

我正在尝试获取 784 MiB 内存。是的,我知道这对于 32 位 phone 来说很多,但以下调用在 Android 5.0 之前有效:

mmap(0, 0x31000000, PROT_NONE, MAP_ANON | MAP_SHARED, -1, 0);

然而,在来自不同制造商的三台不同设备上,升级到 Android5.0 已经打破了这一点。我认为这是 5.0 中内存分配功能的一些变化;也许需要传入不同的标志?

这是 logcat 中返回的错误消息:

E/libc﹕ mmap fail (pid 9994, tid 10125, size 822083584, flags 0x21, errno 12(Out of memory))
  • 重新编译您的应用程序库以匹配棒棒糖并重试
  • 更新您的 SDK
  • 请务必在您的应用程序设置中将目标平台设置为棒棒糖
  • 减少分配的内存并再次检查

您是否尝试过 largeHeap 选项?

In very special situations, you can request a larger heap size by setting the largeHeap attribute to "true" in the manifest tag. If you do so, you can call getLargeMemoryClass() to get an estimate of the large heap size.

https://developer.android.com/training/articles/memory.html#CheckHowMuchMemory

更多详情:

https://developer.android.com/guide/topics/manifest/application-element.html#largeHeap

mmap() 失败的地方,打开 /proc/self/maps 并将内容复制到一个临时文件,然后在编辑器中检查该文件。你应该看到一堆这样的条目:

12e01000-42c00000 ---p 00201000 00:04 11639      /dev/ashmem/dalvik-main space (deleted)
55281000-5d500000 r--s 00000000 00:16 61         /storage/sdcard1/blah
5d500000-67e80000 rw-p 00000000 00:00 0          [anon:libc_malloc]
67ea4000-682cc000 r-xp 00000000 b3:17 114807     /system/vendor/lib/libsc-a3xx.so
682cc000-682f4000 r--p 00427000 b3:17 114807     /system/vendor/lib/libsc-a3xx.so

左边的数字是进程的虚拟地址范围(开始/结束)。当您创建一个新的映射时,它需要适应映射之间的间隙。

在上面的示例中,在 0x42c00000 处的第一个条目的结尾和 0x55281000 处的下一个条目的开头之间有一个很大的差距。那大约是 294MB。后面两个之间没有space,后面只有一个小

如果您查看您的流程图,但没有找到足够大的间隙来容纳您的文件,您就有了答案。 0x00000000 和 0xbfffffff 之间的区域通常可供 32 位应用程序使用,但应用程序框架会占用大量区域。 (顶部 1GB 映射到内核。)

我的猜测是 ASLR and changes to the way virtual memory is allocated in Lollipop have led to this problem. In the map attached to 的某种组合,发现的最大差距约为 300MB。有两个大 "dalvik" 区域,一个 768MB(在 12e01000),一个 1.2GB(在 84d81000)。 (因为你是 运行 Lollipop 这些实际上是由于 ART 而不是 Dalvik,但显然标签卡住了。)

一种可能是 ART 比 Dalvik 具有更高的虚拟内存要求,并且大量分配使得应用程序难以获得大型映射区域。 ART 也可能由于错误而过度分配。您可能想在 Marshmallow 上进行测试,看看是否有问题得到修复。

无论如何,如果没有足够大的连续虚拟内存地址区域来容纳映射,则无法创建映射。由于应用程序框架的使用和另一个问题中看到的大量 ART 分配,即使虚拟地址 space 没有碎片化,768MB 的映射也是不可能的。您将需要映射文件的较小部分,并可能在您腾出空间时取消映射它们。

可能值得在 b.android.com 上提交错误。附上您的流程图文件的副本,并具体说明 Android 和设备的版本。

有关解释 /proc/maps 输出的更多信息,请参见例如this answer.