1. 背景
在使用Kafka的过程中随着集群规模与数据量的增长大部分同学可能都会遇到OutOfMemoryError: Map failed异常,遇到此异常后通过修改系统参数max_map_count值解决。Kafka官网也明确提到部署Kafka集群时尽量把此系统参数调大。笔者从【Java源码层->JVM源码层->系统源码层】由浅入深、层层递进的方式向大家分享OutOfMemoryError: Map failed异常抛出的整个过程。
2. 分析
【1】根据Map failed异常栈在sun.nio.ch.FileChannelImpl.map抛出的异常之所以在此处抛出是因为Kafka利用零拷贝技术来提高处理性能,其中索文件(Index)用MappedByteBuffer实现,日志文件则用FileChannel.transferTo实现。【2】查看sun.nio.ch.FileChannelImpl.map源码调用的是map0本地方法,如果出现OOM后触发FullGC进行垃圾回收释放内存,如果还是超过限制则抛出OOM的Map failed异常。
【3】查看JVM源码中的map0方法,调用mmap64函数出错时抛出OOM的Map failed异常。
【4】JVM源码中调用的mmap64函数是个宏定义,实际调用的是系统函数mmap。
【5】Linux系统函数调用的特点是sys_xxx,根据该特点可轻易找到mmap函数的具体声明
【6】在Linux系统中函数调用都是以SYSCALL_DEFINEx(name…)定义的,x表示参数个数,name表示函数名。根据mmap函数声明的参数个数就知道SYSCALL_DEFINE6的含义了
【7】系统mmap函数主要由sys_mmap_pgoff函数负责实现
【8】调用vm_mmap_pgoff函数,此函数的用途是对进程的虚拟地址空间进行文件映射
【9】调用的do_mmap_pgoff是内联函数具体实现在do_mmap函数中
【10】查看do_mmap函数具体实现,该函数的主要作用是用来将虚拟内存与物理内存进行直接映射,前面是一系列的安全检查包括:页对齐检查、内存检查等等。map_count再超过sysctl_max_count系统配置则直接抛出ENOMEM异常
【11】查看ENOMEM的具体含义是Out of memory
3. 总结
经过层层分析终于知晓在Kafka运行过程中抛出OutOfMemoryError: Map failed异常并调整系统参数 max_map_count值的整个来龙去脉。需要特别强调Linux系统函数do_mmap中包含多项安全检查,只要任何一项检查不通过都会抛出ENOMEM异常,如果系统参数max_map_count调大后问题依然存在可自助查看该函数源码并分析没有通过安全检查的原因。