一键释放iOS 64位App潜力( 三 )

其中vm_map_enter在分配过程中会对hole_entry→vme_end作判断 , vme_end即最大的可分配空间 。
xnu上虚拟内存的分配范围本来我只是观察到苹果在iOS15上增加了com.apple.developer.kernel.increased-memory-limit的能力声明 。本着死马当活马医的想法 , 尝试在新版本上添加该声明以缓解一部分问题 。
结果偶然看到部分开发者提问:该能力可配合com.apple.developer.kernel.extended-virtual-addressing使用 。看到后我一下子反应过来 , 顺手搜到了今年二月国外有大佬做了相关的探索:
Size Matters: An Exploration of Virtual Memory on iOS
文章阐述了iOS的内存管理机制和虚拟内存空间分配在不同的机型上存在上限 , 代码如下:
#define ARM64_MIN_MAX_ADDRESS (SHARED_REGION_BASE_ARM64 + SHARED_REGION_SIZE_ARM64 + 0x20000000) // end of shared region + 512MB for various purposesconst vm_map_offset_t min_max_offset = ARM64_MIN_MAX_ADDRESS; // end of shared region + 512MB for various purposesif (arm64_pmap_max_offset_default) {max_offset_ret = arm64_pmap_max_offset_default;} else if (max_mem > 0xC0000000) {max_offset_ret = min_max_offset + 0x138000000; // Max offset is 13.375GB for devices with > 3GB of memory} else if (max_mem > 0x40000000) {max_offset_ret = min_max_offset + 0x38000000;// Max offset is 9.375GB for devices with > 1GB and <= 3GB of memory} else {max_offset_ret = min_max_offset;}并且总结了一个上限值与机型表格:
RAM
Address Space
Usable
Devices
> 3 GiB
15.375 GiB
7.375 GiB
- iPhone XS – iPhone 13
- iPad Air (4th generation)
- iPad Pro (12.9-inch), (10.5-inch), (11-inch)
> 1 GiB
11.375 GiB
3.375 GiB
【一键释放iOS 64位App潜力】- iPhone 6s – X, SE, XR
- iPad (5th generation) – iPad (8th generation)
- iPad Air 2, iPad Air (3rd generation)
- iPad mini 4, iPad mini (5th generation)
- iPad Pro (9.7-inch)
<= 1 GiB
10.5 GiB
2.5 GiB
- iPhone 5s, iPhone 6
- iPad Air
- iPad mini 2, iPad mini 3
而xnu的源码(pmap.c)中还透露了内核内存分配存在jumbo机制 。当iOS App带有指定的能力声明时 , xnu内核将会以jumbo模式运行 , 虚拟内存地址空间将会直接分配为最大值64GB:
if (option == ARM_PMAP_MAX_OFFSET_JUMBO) {if (arm64_pmap_max_offset_default) {// Allow the boot-arg to override jumbo sizemax_offset_ret = arm64_pmap_max_offset_default;} else {max_offset_ret = MACH_VM_MAX_ADDRESS;// Max offset is 64GB for pmaps with special "jumbo" blessing}}并且该上限值会在进程启动时进行调整 , 具体代码可以在kern_exec.c中找到:
/* * Apply the requested maximum address. */if (error == 0 && imgp->ip_px_sa != NULL) {struct _posix_spawnattr *psa = (struct _posix_spawnattr *) imgp->ip_px_sa;if (psa->psa_max_addr) {vm_map_set_max_addr(get_task_map(new_task), (vm_map_offset_t)psa->psa_max_addr);}}甚少文档记录的entitlementcom.apple.developer.kernel.extended-virtual-addressing
苹果的文档仅有一句话说明该能力:

Use this entitlement if your app has specific needs that require a larger addressable space. For example, games that memory map assets to stream to the GPU may benefit from a larger address space.
举个例子:有的游戏需要将资源通过mmap的形式传递到GPU中渲染时 , 更大的地址空间可提高其运行效率 。
描述上看 , 配置该选项时 , 将开启上面xnu的jumbo mode , 地址的扩充刚好能解决上面的崩溃问题 。
做一次极限测试为验证地址分配的极限值 , 简单做个实验(测试设备使用iPhone XR iOS 16 Beta 2):
通过malloc进行连续的内存分配(也可以用vm_allocate , 阈值不一样) , 阈值卡在1009字节(为什么是1009字节 , 这里可以参考【ios 内核】源码解读(3) 详解ios是怎么malloc的(上) - 钟路成的博客 (luchengzhong.github.io)) 。
for (size_t i = 0; i < SIZE_T_MAX; i++) {void *a = malloc(1009);if (a == NULL) {NSLog(@"error count: %lu", i);break;}}结果如下:
size = 1009 > SMALL_THRESHOLD (64位系统下1008字节 , 32位系统下496)
内存扩展前malloc失败阈值约 7065482 * 1009 = 6.63 GB
内存扩展后malloc失败阈值约 56753881 * 1009 = 53.33 GB
当然 , 在xnu的单元测试代码中 , 也可找到jumbo mode相关的测试代码 , 与上面的测试结果完全一致 , 即最多可分配53GB的空间 。


推荐阅读