性能优化2 内存分配相关
并非原创文章,仅作为 极客时间 课程学习笔记
一个内存问题
一个 Linux 系统中,设置了 JVM 最大堆内存为 8GB,但在近百个并发线程下, Java 进程占用了 14 GB 的内存,为什么呢?
因为 Java 是通过 C 语言来预分配内存作为内存池。预分配的 6GB 内存和 JVM 中的 8GB 内存叠加在一起,造成了 Java 进程占用 14 GB 内存。
内存池
当申请内存时,会先到应用层内存池申请,如果不够,则向下一级的 C 库内存池申请,如果还不够,则向操作系统内核申请。Java 除了 JVM 管理的堆内存之外,还有一些堆外内存,这些堆外内存不受 JVM 限制,更稳定持久,处理 IO 也更快。这些堆外内存由 C 库内存池负责分配。
C 库内存池在分配时,会分配比申请的大得多的内存。并且在应用程序释放此块内存时,并不会归还给操作系统内核。并且,在多线程下,每个子线程会预分配 64 MB 内存,子线程内存池数量最多是八倍的 CPU 核心数。
👆上面的那个内存问题出现原因就是因为, JVM 编译时使用了 PTmalloc2 内存池,上百个 Java 线程预分配了 6 GB 的内存。可以使用 MALLOC_ARENA_MAX 环境变量来限制线程内存池的最大数量。
Ptmalloc2 和 TCMalloc
TCMalloc 适用于大量并发线程,并且每个线程使用内存较小的情况
Ptmalloc2 适用于大内存分配
在堆还是栈上分配内存
栈上分配比堆上分配更快,因为每个线程有独立的栈,所以分配内存不需要加锁。
但是栈生命周期优先,函数调用结束就自动释放。栈容量也有限(CentOS 中为 8 MB),很容易栈溢出。