https://juejin.cn/post/7011002046899978253

一、前言

最近花了很多时间在 JVM 的内存预留代码上。它开始是因为我们得到了外部贡献,以支持在 Linux 上使用多个大小的 large page。为了以一种好的方式做到这一点,必须首先重构一些其他的东西。在沿着 内存通道 进行这次旅行时,我意识到对 JVM 使用的  large pages 做一个简短的总结可能是一篇非常有趣的阅读。

二、large pages 介绍

在我们开始讨论 JVM 如何使用它们之前,让我们先简要介绍一下什么是 large pages(大页)。

Large pages 或者叫 huge pages, 是一种减少处理器 TLB 缓存压力的技术。这些缓存用于加快将虚拟地址转换为物理内存地址的时间。大多数体系结构支持多种 page 大小,通常基页大小为 4 KB。对于使用大量内存的应用程序,例如大型 Java 堆,使用更大的 page 粒度映射内存以增加 TLB 中的命中率是有意义的。在 x86-64 上,2 MB 和 1 GB page 可用于此目的,对于内存密集型工作负载,这可能会产生非常大的影响。

在上图中,我们可以看到在使用和不使用大页面时运行几个 SPECjbb 1基准测试的差异。配置上的唯一区别是高性能 JVM 启用了 large pages。结果非常令人印象深刻,对于许多 Java 工作负载来说,启用 large pages 是一个巨大的胜利。

三、启用 large pages

为 Java 启用 large pages 的通用开关是-XX:+UseLargePages,但要利用 large pages,还需要正确配置操作系统。让我们看看如何在 Linux 和 Windows 中配置。

3.1 Linux

在 Linux 上,JVM 可以通过两种不同的方式使用 large pages:Transparent Huge Pages 和 HugeTLB pages。它们在配置方式上有所不同,但在性能特征上也略有不同。

3.1.1 Transparent Huge Pages (THP)

Transparent Huge Pages,简称 THP,是一种在 Linux 中简化 large pages 使用和启用的方法。启用后,Linux 内核将尝试使用 large pages 来保留足够大且有资格使用 THP。可以在三个不同级别配置 THP 支持:

  • always - 任何应用程序都会自动使用 transparent huge pages。
  • madvise - transparent huge pages 仅在应用程序使用 [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) 标志 MADV_HUGEPAGE 来标记某些内存段应由 large pages 支持时才使用。
  • never - 从不使用 transparent huge pages。

配置存储在 /sys/kernel/mm/transparent_hugepage/enabled 可以像这样轻松更改:

 
bash

复制代码
$ echo "madvise" > /sys/kernel/mm/transparent_hugepage/enabled

JVM 支持在 madvise mode 中配置时使用 THP ,但需要使用 -XX:+UseTransparentHugePages。完成此操作后,Java 堆以及其他内部 JVM 数据结构将由 transparent huge pages 支持。

为了使内核能够满足使用 transparent huge pages 的请求,需要有足够的连续物理内存可用。如果没有内核将尝试对内存进行碎片整理以满足请求。碎片整理可以通过几种不同的方式进行配置,当前策略存储在/sys/kernel/mm/transparent_hugepage/defrag. 有关此配置和其他配置的更多详细信息,请参阅 kernel 内核文档

3.1.2 HugeTLB pages

这种类型的 large pages 由操作系统预先分配并消耗用于支持它们的物理内存。应用程序可以使用mmap()标志从这个池中保留 page MAP_HUGETLB。这是在 Linux 上为 JVM 使用 large pages 的默认方式,可以通过设置-XX:+UseLargePages或特定标记启用-XX:+UseHugeTLBFS

当 JVM 使用这种类型的 large pages 时,它会在前面提交由 large pages 支持的整个内存范围。这是确保没有其他预留耗尽操作系统分配的 large pages 池所必需的。这也意味着需要预先分配足够的 large pages 以在保留时支持整个内存范围,否则 JVM 将回退到使用正常 page。

要配置这种类型的 large pages,首先检查可用的 page 大小:

 
bash

复制代码
$ ls /sys/kernel/mm/hugepages/
hugepages-1048576kB hugepages-2048kB

然后根据你的需求配置 page 大小,如下所示:

 
bash

复制代码
$ echo 2500 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

这会尝试分配 2500 个 2 MB 的 pages。应该始终读取实际存储在 nr_hugepages 中的值,以确保内核能够分配请求的数量。

默认情况下,JVM 将在尝试保留 large pages 时使用系统环境默认 large page 大小,你可以通过下面的方式查看系统默认的 large page 大小:

 
bash

复制代码
$ cat /proc/meminfo | grep Hugepagesize
Hugepagesize: 2048 kB

如果您想使用不同的 large page 大小,可以通过设置 JVM LargePageSizeInBytes 标记来完成 。例如:使用 1 GB 的页面-XX:LargePageSizeInBytes=1g

更多关于 HugeTLB pages 的信息也可以查看 kernel 内核文档

3.1.3 使用哪一个

两种方法各有利弊,选择哪一种取决于多个方面。THP 更易于设置和使用,但在使用 HugeTLB 页面时你有更多控制权。如果延迟是你最关心的问题,那么你可能应该使用 HugeTLB 页面,因为你永远不会等待操作系统释放足够的连续内存。作为替代方案,你可以将 defrag THP 选项配置为在没有 large pages 可用时不停止,但这可能会带来吞吐量成本。如果内存占用是一个问题,THP 是避免必须预先提交整个 Java 堆的更好选择。

使用哪种类型的 large pages 取决于应用程序和环境,但在很多情况下,使用任何类型的 large pages 都会对性能产生积极影响。

3.2 Windows

在 Windows 上,配置步骤要容易一些,至少在较新的版本上是这样。运行要使用 large pages 的进程的用户需要具有 锁定内存页 权限。在 Windows 10 上,这是通过以下方式完成的:

  1. 运行 gpedit.msc
  2. 在 “计算机配置” > “Windows 设置” > “安全设置” > “本地策略” 下找到 “用户权限分配”
  3. 找到 “在内存中锁定页面” 并双击它
  4. 单击 “添加用户或组” 并添加正确的用户
  5. 注销或重启电脑

一旦授予此权限,JVM 将能够使用 large pages(如果使用-XX:+UseLargePages. Windows 上的 JVM large pages 实现与 Linux 上 HugeTLB pages 非常相似。由 large pages 支持的整个预留是预先提交的,以确保我们以后不会出现任何故障。

直到最近,有一个 bug 阻止 G1(默认 GC)在 Windows 上为大于 4 GB 的堆使用 large pages。现在已修复此问题,并且继续使用G1运行大型Minecraft 服务器应该能够通过启用 large pages ** 获得不错的提升**。

四、检查 JVM

一旦你的环境配置正确并且你已经在运行时启用 Java  large pages,最好验证 JVM 是否真的使用了 large pages。你可以使用你最喜欢的操作系统工具来检查这一点,但 JVM 也有一些日志记录选项可以帮助解决这个问题。要查看一些基本的 GC 配置,你可以使用-Xlog:gc+init。使用 G1 你可以看到以下输出:

 
bash

复制代码
> jdk-16/bin/java -Xlog:gc+init -XX:+UseLargePages -Xmx4g -version
[0.029s][info][gc,init] Version: 16+36-2231 (release)
[0.029s][info][gc,init] CPUs: 40 total, 40 available
[0.029s][info][gc,init] Memory: 64040M
[0.029s][info][gc,init] Large Page Support: Enabled (Explicit)
[0.029s][info][gc,init] NUMA Support: Disabled
[0.029s][info][gc,init] Compressed Oops: Enabled (Zero based)
[0.029s][info][gc,init] Heap Region Size: 2M
[0.029s][info][gc,init] Heap Min Capacity: 8M
[0.029s][info][gc,init] Heap Initial Capacity: 1002M
[0.029s][info][gc,init] Heap Max Capacity: 4G
[0.029s][info][gc,init] Pre-touch: Disabled
[0.029s][info][gc,init] Parallel Workers: 28
[0.029s][info][gc,init] Concurrent Workers: 7
[0.029s][info][gc,init] Concurrent Refinement Workers: 28
[0.029s][info][gc,init] Periodic GC: Disabled

这是在 Linux 上运行的,我们可以看到启用了 Large Page 支持Explicit 意味着使用了 HugeTLB 页面。如果使用-XX:+UseTransparentHugePages日志行运行将如下所示:

 
bash

复制代码
[0.030s][info][gc,init] Large Page Support: Enabled (Transparent)

以上仅显示是否启用了 large pages,如果你想了解有关使用 large pages JVM 的更多详细信息,你可以启用-Xlog:pagesize并获得如下输出:

 
bash

复制代码
[0.002s][info][pagesize] CodeHeap 'non-nmethods':  min=2496K max=8M base=0x00007fed3d600000 page_size=4K size=8M
[0.002s][info][pagesize] CodeHeap 'profiled nmethods': min=2496K max=116M base=0x00007fed3de00000 page_size=4K size=116M
[0.002s][info][pagesize] CodeHeap 'non-profiled nmethods': min=2496K max=116M base=0x00007fed45200000 page_size=4K size=116M
[0.026s][info][pagesize] Heap: min=8M max=4G base=0x0000000700000000 page_size=2M size=4G
[0.026s][info][pagesize] Block Offset Table: req_size=8M base=0x00007fed3c000000 page_size=2M alignment=2M size=8M
[0.026s][info][pagesize] Card Table: req_size=8M base=0x00007fed3b800000 page_size=2M alignment=2M size=8M
[0.026s][info][pagesize] Card Counts Table: req_size=8M base=0x00007fed3b000000 page_size=2M alignment=2M size=8M
[0.026s][info][pagesize] Prev Bitmap: req_size=64M base=0x00007fed37000000 page_size=2M alignment=2M size=64M
[0.026s][info][pagesize] Next Bitmap: req_size=64M base=0x00007fed33000000 page_size=2M alignment=2M size=64M

这是非常详细的信息,它是验证 JVM 的哪些部分由 large pages 支持的好方法。上面的输出是使用 JDK 16 生成的,它有一个 bug 导致 CodeHeap 页面大小不正确,它们也由 large pages 支持。

五、译者说

这篇文章是另外一篇 Java17 GC 文章的前置篇,我们已经陆续发了多篇 Java17 相关文章,还有一批文章正在酝酿。

另外笔者开源的微服务组件 mica 已经适配 java17。关注我,学习技术不迷路!

[转帖]Java 提速之 Large pages【译】的更多相关文章

  1. [转]Part2: Understanding !PTE, Part2: Flags and Large Pages

    http://blogs.msdn.com/b/ntdebugging/archive/2010/04/14/understanding-pte-part2-flags-and-large-pages ...

  2. [转帖]java面试和笔试大全

    java面试和笔试大全 https://www.cnblogs.com/linzheng/archive/2011/01/05/1926856.html 2.String是最基本的数据类型吗? 基本数 ...

  3. Java – Reading a Large File Efficiently--转

    原文地址:http://www.baeldung.com/java-read-lines-large-file 1. Overview This tutorial will show how to r ...

  4. [精华帖]Java接口怎么定义?如何使用?【实例讲解】

    [精华帖?]滑稽之谈||| 题目: 模拟电脑USB功能设备使用 1.定义USB接口,具备最基本的开启功能和关闭功能 2.定义电脑类,具有开机.关机以及使用usb设备功能 3.鼠标类.具有usb功能,并 ...

  5. Java垃圾回收介绍(译)

    在Java中,对象内存空间的分配与回收是由JVM中的垃圾回收进程自动完成的.与C语言不同的是,在Java中开发者不需要专门为垃圾回收写代码.这是使Java流行的众多特征之一,也帮助了程序员写出了更好的 ...

  6. Effective java 系列之异常转译

    异常转译:当位于最上层的子系统不需要关心底层的异常细节时,常见的作法时捕获原始异常,把它转换一个新的不同类型的异常,在将新异常抛出. 通常方法捕获底层异常,然后抛高层异常. public static ...

  7. Java EE平台介绍(译)

    Java EE平台介绍 2.1 企业应用总览 这一部分将对企业应用及其设计和开发进行简单介绍. 就像之前说的,Java EE 平台是为了帮助开发者开发大规模.多层次.可伸缩.服务可靠.网络安全的应用而 ...

  8. [转帖]java注解核心知识总结

    java注解核心知识总结 2019-11-01 20:39:50 从事Java 阅读数 2  收藏 文章标签: java注解总结程序员 更多 分类专栏: java 注解   版权声明:本文为博主原创文 ...

  9. [转帖]Java Netty简介

    Java Netty简介 https://www.cnblogs.com/ghj1976/p/3779820.html Posted on 2014-06-10 13:41 蝈蝈俊 阅读(2992) ...

  10. [转帖]Java中重写和重载与多态的关系

    Java中重写和重载与多态的关系 2019-09-05 00:57:41 留下一天今天 阅读数 67  收藏 更多 分类专栏: java进阶之路   版权声明:本文为博主原创文章,遵循CC 4.0 B ...

随机推荐

  1. pyecharts 保存图表至本地

    pip install snapshot-selenium from pyecharts.render import make_snapshot from snapshot_selenium impo ...

  2. Windows10安装WSL2和Ubuntu的过程

      因为在Windows10环境中安装了2个相同的包导致冲突,所以想到了通过WSL2+Docker的方式进行编程开发.因为Docker Desktop直接安装就行了,不做介绍.本文主要介绍WSL2和U ...

  3. 5大特性,带你认识化繁为简的华为云CodeArts Deploy

    摘要:2月27日,华为云发布持续部署服务CodeArts Deploy,通过模块化自由编排部署流程,实现软件的自动化部署,帮助企业软件产品的快速.高效.高质量交付. 本文分享自华为云社区<化繁为 ...

  4. 一个合格的CloudNative应用:程序当开源软件编写,应用配置外置

    摘要:对于一个合格的CloudNative应用,应该把自己的程序当做开源软件来编写的,不该将数据库连接信息和密码放在代码里,一定要将配置外置. 对于一个合格的CloudNative应用,应该把自己的程 ...

  5. 一段java代码是如何执行的?

    摘要:当你学会了java语言之后,你写了一些代码,然后你想要执行你的代码,来达成某些功能.那么,你都知道这段java代码都是如何执行的吗? 本文分享自华为云社区<一段java代码是如何执行的&g ...

  6. 关于单元测试的那些事儿,Mockito 都能帮你解决

    摘要:相信每一个程序猿在写Unit Test的时候都会碰到一些令人头疼的问题:如何测试一个rest接口:如何测试一个包含客户端调用服务端的复杂方法:如何测试一个包含从数据库读取数据的复杂方法...这些 ...

  7. 传统到敏捷的转型中,谁更适合做Scrum Master?

    摘要:本文主要讲述的是从传统到敏捷Scrum团队转型中,对Scrum Master这一角色的分析. 本文分享自华为云社区<传统到敏捷的转型中,谁更适合做Scrum Master?>,作者: ...

  8. docker镜像列表存在但删除显示 No such image问题解决

    近期使用了docker,但删除镜像时候遇到了无法删除问题.提示:No such Image.原因有两个,解决方法如下: 原因1: 容器还存在是无法删除镜像的 解决步骤: 1.停掉容器(docker s ...

  9. Solon 生态插件清单

    Solon 是一个微型的Java开发框架.强调,克制 + 简洁 + 开放的原则:力求,更小.更快.更自由的体验.支持:RPC.REST API.MVC.Micro service.WebSocket. ...

  10. ElasticSearch 实现分词全文检索 - Java SpringBoot ES 索引操作

    目录 ElasticSearch 实现分词全文检索 - 概述 ElasticSearch 实现分词全文检索 - ES.Kibana.IK安装 ElasticSearch 实现分词全文检索 - Rest ...