为 Java 启用大页面支持
Oracle G1GC Tuning中提到,使用大页面可以提高吞吐量,在这里记录一下Linux
服务器的开启方式
本文基于Ubuntu 18.04 LTS
、OpenJDK 11.0.4
编写,不同版本操作可能稍有不同
为操作系统启用大页面
准备工作
执行cat /proc/meminfo | grep -i HugePages
,如果有类似如下输出,则表示服务器支持大页面:
1 | AnonHugePages: 1013760 kB |
可以看到,大页面每个页面的大小为2048 kB
你需要根据具体需求来计算你需要多少大页面,如你应用的堆为4GB
,按照上面2048 kB
的大页面大小,你一共需要4 * 1024 * 1024 / 2048 = 2048
个大页面
如果你希望元空间也使用大页面,则也需要一并计算在内,如你的元空间占用200MB
,则建议预留200 * 1024 / 2048 = 100
个大页面,两者加起来一共需要2148
个大页面
为了避免出现大页分配失败的警告,建议在实际需要的页面数上再额外加一些,如64
个页面,那么对于上述例子,我们一共需要2212
个大页面,共占用4424MB
启用大页面
需要注意的是,大页面即使未使用,也会一直占用你的内存,因此,你可能需要先停止正在运行的服务来提供充足的内存空间
执行echo <页面数> > /proc/sys/vm/nr_hugepages
来生成大页面,如上面的例子,我们应该执行echo 2212 > /proc/sys/vm/nr_hugepages
执行完毕后,可以再次执行cat /proc/meminfo | grep -i HugePages
来查看申请的大页面:
1 | AnonHugePages: 1013760 kB |
如果你发现申请到的大页面数量和你预期的不符,则可能是因为系统剩余内存不足,这时建议先关闭正在运行的应用,释放充足的内存后重新生成大页面
开机自动设置
上面启用大页面的方法在重启后就会失效,重启后你会发现大页面数又重新变回了0
因此我们需要设置一个脚本来在开机时自动生成大页面:
新建一个启用大页面的脚本,如我放在了
/root/shells/hugepages
:1
2
3
4
5
6
7
8
### BEGIN INIT INFO
# Provides: hugepages
# Default-Start: 2 3 4 5
### END INIT INFO
echo 2176 > /proc/sys/vm/nr_hugepages设置这个脚本开机自启动
1
2
3cd /etc/init.d
ln -s /root/shells/hugepages hugepages
update-rc.d hugepages defaults
这样,下次重启后就会自动生成大页面了
ulimit
根据Oracle HugePages说明,应该将ulimit
中的memlock
增加到至少为内存大小的90%
,这个数字是以kB
为单位的
例如,对于64GB
内存的机器,这个值应该为64 * 1024 * 1024 * 0.9 = 60397977.6
,去掉小数为60397977
接着,在/etc/security/limits.conf
中添加下面两行即可:
1 | * soft memlock 60397977 |
在运行你的应用之前,你应该先退出当前Shell
并重新登陆来使修改生效
特别的,对于用root
用户运行的应用,上述设置是无效的,应在/etc/security/limits.conf
中添加下面两行:
1 | root soft memlock 60397977 |
禁用大页面
如果你不再需要使用大页面了,可以先关闭所有使用大页面的应用,然后执行echo 0 > /proc/sys/vm/nr_hugepages
来删除所有大页面
如果你使用了开机自动设置大页面,则可执行update-rc.d hugepages remove
来取消自动设置
为 Java 应用设置大页面
在你的启动参数中增加-XX:+UseLargePages
即可启用大页面,如果你还想为元空间启用大页面,则需要增加XX:+UseLargePagesInMetaspace
对于内存占用较大的应用,启用大页面后你会观察到它的内存占用量大幅下降,这是正常的,因为通过大页面占用的内存是不统计在进程的res
中的
如果你看到类似下面这种警告:
1 | Java HotSpot(TM) 64-Bit Server VM warning: Failed to reserve large pages memory req_addr: 0x0000000000000000 bytes: 8388608 (errno = 12). |
这说明你的大页面数量不足,你可以尝试降低Java堆
大小或增加大页面数来解决