为何macOS的virtual_size与Linux的VmSize差异超千倍?
macOS与Linux进程虚拟内存统计差异解析
我发现macOS中通过MACH_TASK_BASIC_INFO获取的virtual_size,与Linux中/proc/self/status报告的VmSize理论上属于等价指标,但实际测试数值差异超千倍:常驻内存差异合理,但macOS的虚拟内存数值超过4GB,明显不符合预期。以下是测试细节及问题解析:
测试程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #ifdef __APPLE__ #include <mach/mach.h> #include <mach/task_info.h> #include <mach/mach_error.h> #endif #define MEGABYTE 1048576.0 #ifdef __linux__ typedef struct { size_t virtual_size; size_t resident_size; size_t resident_size_max; } memory_stats_t; #endif int main(void) { #if defined(__linux__) memory_stats_t stats = {}; FILE* file = fopen("/proc/self/status", "r"); if (file == NULL) return perror("fopen"), EXIT_FAILURE; char line[256]; while (fgets(line, sizeof(line), file)) { if (sscanf(line, "VmSize: %lu kB", &stats.virtual_size) == 1) { stats.virtual_size *= 1024; } else if (sscanf(line, "VmRSS: %lu kB", &stats.resident_size) == 1) { stats.resident_size *= 1024; } else if (sscanf(line, "VmHWM: %lu kB", &stats.resident_size_max) == 1) { stats.resident_size_max *= 1024; } } fclose(file); #elif defined(__APPLE__) task_t task = MACH_PORT_NULL; kern_return_t kr = task_for_pid(mach_task_self(), getpid(), &task); if (kr != KERN_SUCCESS) { return fprintf(stderr, "task_for_pid error: %s\n", mach_error_string(kr)), EXIT_FAILURE; } mach_task_basic_info_data_t stats; mach_msg_type_number_t task_info_count = MACH_TASK_BASIC_INFO_COUNT; kr = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t)&stats, &task_info_count); if (kr != KERN_SUCCESS) { return fprintf(stderr, "task_info error: %s\n", mach_error_string(kr)), EXIT_FAILURE; } #else # error "Unsupported OS" #endif // Print memory usage information printf("=== Current process memory usage [PID: %d]\n", getpid()); printf("Virtual: %.3f MB\n", stats.virtual_size / MEGABYTE); printf("Resident: %.3f MB\n", stats.resident_size / MEGABYTE); printf("Resident-Peak: %.3f MB\n", stats.resident_size_max / MEGABYTE); return EXIT_SUCCESS; }
编译运行命令
gcc -o memusage memusage.c && ./memusage(macOS环境下的gcc实际为Apple Clang)
测试输出
Linux下输出
=== Current process memory usage [PID: 1403418] Virtual: 2.594 MB Resident: 1.125 MB Resident-Peak: 1.125 MB
macOS下输出
=== Current process memory usage [PID: 68603] Virtual: 4169.246 MB Resident: 0.699 MB Resident-Peak: 0.699 MB
核心差异解析
1. 虚拟内存统计定义不同
- Linux的VmSize:统计进程实际已分配的虚拟地址空间总和,包含已映射的文件、匿名内存、栈、堆等所有被进程主动使用的虚拟内存区域,不包含未被分配的预留地址空间。
- macOS的MACH_TASK_BASIC_INFO.virtual_size:统计的是进程整个虚拟地址空间的跨度——即从进程虚拟地址的最低有效位到最高有效位的总范围,其中包含了大量未被实际分配、仅为未来使用预留的地址空间(64位macOS进程默认会预留极大的虚拟地址范围),因此数值会远高于实际使用量。
2. macOS替代API:获取接近Linux VmSize的统计值
如果需要在macOS上获取与Linux VmSize定义相近的虚拟内存使用量,可以使用task_vm_info接口,其中的used_address_space字段代表进程实际已使用的虚拟内存大小,与Linux VmSize的统计逻辑一致。
修改后的macOS代码片段示例:
#elif defined(__APPLE__) task_t task = MACH_PORT_NULL; kern_return_t kr = task_for_pid(mach_task_self(), getpid(), &task); if (kr != KERN_SUCCESS) { return fprintf(stderr, "task_for_pid error: %s\n", mach_error_string(kr)), EXIT_FAILURE; } task_vm_info_data_t stats_vm; mach_msg_type_number_t task_info_count = TASK_VM_INFO_COUNT; kr = task_info(task, TASK_VM_INFO, (task_info_t)&stats_vm, &task_info_count); if (kr != KERN_SUCCESS) { return fprintf(stderr, "task_info error: %s\n", mach_error_string(kr)), EXIT_FAILURE; } // 映射到统一结构以便打印 struct { size_t virtual_size; size_t resident_size; size_t resident_size_max; } stats = { .virtual_size = stats_vm.used_address_space, .resident_size = stats_vm.resident_size, .resident_size_max = stats_vm.high_watermark }; #endif
替换后重新编译运行,macOS的虚拟内存数值会和Linux的VmSize处于同一量级,符合预期。
内容的提问来源于stack exchange,提问作者Simon Kissane
相关产品推荐
相关产品推荐

