JVM 剖析花园:2 - 透明大页

💡 原文中文,约5900字,阅读约需14分钟。
📝

内容提要

本文介绍了大页和透明大页的概念以及它们对虚拟内存的影响。大页减少页表遍历成本,提高性能。透明大页为应用程序提供大容量页面支持,不改变应用程序。实验证明启用透明大页显著提高性能。建议在应用程序有大量数据和大堆的情况下尝试使用大页。

🔎

延伸解读

大页为何能提升性能:TLB与页表遍历

文章指出,虚拟地址转换依赖页表遍历,而TLB容量有限(通常不到100个条目),因此TLB未命中会成为性能瓶颈。使用更大的页(如2M/4M/1G)覆盖相同内存区域,既能减少页表项数量、降低页表遍历成本,也能减少TLB未命中。实验数据显示,在100MB数组随机访问场景下,启用透明大页后dTLB负载未命中从每次约1次降至接近0,耗时从32.5ns/op降至28.7ns/op。

两种Linux大页实现:hugetlbfs与THP的取舍

Linux提供hugetlbfs和透明大页两种方式。hugetlbfs需预留内存并修改应用,且分配的空间不能被普通进程使用,属于“要么全有,要么全无”。透明大页则对应用透明,但可能带来内存开销(小文件占用整个大页)或时间开销(碎片整理)。文章建议通过madvise(2)让应用指定使用THP的区域,以平衡收益与开销。

JVM参数选择:UseTransparentHugePages与UseLarge

OpenJDK提供三个相关参数:-XX:+UseHugeTLBFS将Java堆映射到hugetlbfs;-XX:+UseTransparentHugePages提示堆使用THP;-XX:+UseLargePages是通用快捷方式,但在Linux上实际启用的是hugetlbfs而非THP。文章认为THP选项很方便,因为Java堆大且连续,可能从大页中获益最多。

实践建议:大堆应用可尝试,注意碎片整理成本

实验表明,在数组大小超过L3缓存(如1MB以上)时,大页带来明显性能提升,且THP与hugetlbfs效果相当。文章建议在应用有大量数据和大堆时尝试大页,并推荐启用-XX:+AlwaysPreTouch将碎片整理成本转移到启动阶段。同时提醒,THP的碎片整理可能让短暂应用变慢,需结合场景评估。

❓

Q&A

什么是大页和透明大页?它们有什么区别?

大页(Large Page)是比常规4K页更大的内存页(如2M/4M/1G),可减少页表遍历成本,提高性能。透明大页(Transparent Huge Page, THP)是Linux的一种实现,自动为应用程序提供大页支持,无需修改应用程序;而hugetlbfs需要预先分配和应用程序更改。

透明大页对JVM应用程序有什么好处?

透明大页可以减少TLB未命中,从而提升性能,尤其对于大堆和大量数据的应用。实验表明,启用THP后,随机访问大数组的耗时明显降低(例如100M数组从32.5ns/op降至28.7ns/op),并且使用-XX:+AlwaysPreTouch时初始化更快,内存释放也更快。

如何在Linux上为JVM启用透明大页?

可以通过JVM参数-XX:+UseTransparentHugePages启用THP,或使用-XX:+UseLargePages(在Linux上启用hugetlbfs)。同时需要系统配置:将/sys/kernel/mm/transparent_hugepage/enabled设置为madvise或always,并可能调整defrag设置。

启用透明大页有什么潜在风险或开销?

THP可能导致内存开销(为小文件分配整个大页)或时间开销(内存碎片整理)。对于短生命周期的应用,碎片整理成本可能超过收益。可以通过madvise(2)让应用选择性使用THP,或使用-XX:+AlwaysPreTouch在启动时预先触碰堆以转移开销。

透明大页和hugetlbfs在JVM中如何选择?

hugetlbfs需要预先分配内存且应用程序需修改,是“全有或全无”的;THP则透明自动,无需修改应用。JVM中-XX:+UseTransparentHugePages启用THP,-XX:+UseHugeTLBFS启用hugetlbfs,-XX:+UseLargePages是通用快捷方式(在Linux上启用hugetlbfs)。实验显示两者性能提升相似,但THP更易用。

透明大页为什么能提升性能?背后的原理是什么?

大页减少了页表遍历的成本,因为覆盖相同区域需要更少的页表项;同时TLB可以缓存更多地址翻译,减少TLB未命中。实验通过硬件计数器证实,启用THP后dTLB-load-misses从约1次/操作降至接近0。

🏷️

标签

➡️

继续阅读