One, foreword

I’ve spent a lot of time recently on the MEMORY reservation code for the JVM. It started because we got external contributions to support the use of large pages with multiple sizes on Linux. In order to do this in a good way, something else must be refactored first. As I made this trip down the memory channel, I realized that a brief summary of the large Pages used by the JVM could be a very interesting read.

Two, large pages introduction

Before we start talking about how JVMS use them, let’s take a quick look at what large pages are.

Large Pages, or Huge Pages, is a technique to reduce the stress on the PROCESSOR’s TLB cache. These caches are used to speed up the translation of virtual addresses into physical memory addresses. Most architectures support a variety of page sizes, typically a base page size of 4 KB. For applications that use a lot of memory, such as large Java heaps, it makes sense to map memory with greater page granularity to increase hit ratios in TLBS. On x86-64, 2 MB and 1 GB pages are available for this purpose, which can have a very large impact on memory-intensive workloads.

In the figure above, we can see the difference between running several SPECjbb® 1 benchmarks with and without large pages. The only difference in configuration is that high performance JVMS enable Large Pages. The results are very impressive, and enabling Large Pages is a big win for many Java workloads.

Enable Large Pages

The common switch to enable LargePages for Java is -xx :+UseLargePages, but to take advantage of LargePages, you also need to configure the operating system correctly. Let’s look at how to configure in Linux and Windows.

The 3.1 Linux

On Linux, the JVM can use large Pages in two different ways: Transparent Huge Pages and HugeTLB Pages. They differ in the way they are configured, but they also differ slightly in their performance characteristics.

3.1.1 Transparent Huge Pages (THP)

Transparent Huge Pages, or THP for short, is a way to simplify the use and enable of large Pages in Linux. When enabled, the Linux kernel will try to use Large Pages to remain large enough to qualify for THP. THP support can be configured at three different levels:

  • always– Transparent Huge Pages is automatically used by any application.
  • madvise– Transparent Huge Pages Is used only in applications[madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html)markMADV_HUGEPAGETo mark memory segments that should be supported by Large Pages.
  • never– Never use Transparent Huge Pages.

Configuration is stored in the/sys/kernel/mm/transparent_hugepage/enabled can easily change like this:

$ echo "madvise" > /sys/kernel/mm/transparent_hugepage/enabled
Copy the code

The JVM supports using THP when configured in Madvise Mode, but requires -xx :+UseTransparentHugePages. When this is done, the Java heap and other internal JVM data structures will be supported by Transparent Huge Pages.

In order for the kernel to be able to meet requests using Transparent Huge Pages, there needs to be enough contiguous physical memory available. If there is no kernel, an attempt will be made to defragment memory to satisfy the request. Defragmentation can be configured in several different ways, the current policy is stored in the/sys/kernel/mm/transparent_hugepage/defrag. For more details on this and other configurations, see the kernel kernel documentation.

3.1.2 HugeTLB pages

Large pages of this type are pre-allocated by the operating system and consume the physical memory used to support them. An application can reserve the Page MAP_HUGETLB from this pool using the mmap() flag. This is the default way to UseLargePages for the JVM on Linux, and can be enabled by setting -xx :+UseLargePages or specific tags.

When the JVM uses this type of Large Pages, it commits the entire memory range supported by The Large Pages first. This is necessary to ensure that no other reservations deplete the large Pages pool allocated by the operating system. This also means that enough large Pages need to be pre-allocated to support the entire memory range when reserved, or the JVM will fall back to using normal pages.

To configure this type of large Pages, first check the available page size:

$ ls /sys/kernel/mm/hugepages/
hugepages-1048576kB  hugepages-2048kB
Copy the code

Then configure the page size according to your requirements, as shown below:

$ echo 2500 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
Copy the code

This will attempt to allocate 2500 2 MB pages. The value actually stored in NR_HUgePages should always be read to ensure that the kernel allocates the number of requests.

By default, the JVM will use the system environment’s default Large Page size when it tries to preserve large Pages. You can view the system’s default Large Page size in the following way:

$ cat /proc/meminfo | grep Hugepagesize
Hugepagesize:       2048 kB
Copy the code

If you want to use different LargePage sizes, you can do so by setting the JVM LargePageSizeInBytes flag. For example, use 1 GB pages. -xx :LargePageSizeInBytes=1 GB.

More information about HugeTLB Pages can also be found in the kernel documentation.

3.1.3 Which one to use

Both methods have advantages and disadvantages, and the choice of which one to choose depends on many aspects. THP is easier to set up and use, but you have more control when using HugeTLB pages. If latency is your biggest concern, then you should probably use the HugeTLB page, since you’ll never wait for the operating system to free up enough contiguous memory. As an alternative, you can configure the defrag THP option to not stop when no large Pages are available, but this can introduce throughput costs. If memory footprint is an issue, THP is a better option to avoid having to commit the entire Java heap up front.

Which type of Large Pages you use depends on your application and environment, but in many cases, using any type of Large Pages can have a positive impact on performance.

3.2 Windows

On Windows, the configuration steps are easier, at least on newer versions. Users running processes that use Large Pages need to have lock page privileges. On Windows 10, this is done by:

  1. rungpedit.msc
  2. Choose Computer Configuration > Windows Settings > Security Settings > Local Policies to find User Rights Assignment
  3. Locate “Lock page in memory” and double-click it
  4. Click Add User or Group to add the correct user
  5. Log out or restart the computer

Once granted, the JVM will be able to UseLargePages (if using -xx :+UseLargePages. The JVM large pages implementation on Windows is very similar to HugeTLB pages on Linux. The entire reservation, supported by Large Pages, is pre-committed to make sure we don’t have any glitches later.

Until recently, there was a bug that prevented G1 (the default GC) from using large Pages on Windows for heaps larger than 4 GB. This has now been fixed, and continuing to run large Minecraft servers using G1 should provide a nice boost ** by enabling large Pages **.

4. Check the JVM

Once your environment is configured correctly and you have Java Large Pages enabled at run time, it is a good idea to verify that the JVM actually uses Large Pages. You can use your favorite operating system tool to check for this, but the JVM also has some logging options that can help with this. To see some basic GC configuration, you can use -xlog: GC +init. With G1 you can see the following output:

> 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: Explicit (Explicit) [0.029s][info][GC,init] NUMA Support: Explicit Disabled [0.029s][info][GC,init] Compressed Oops: Enabled (Zero based) [0.029s][info][GC,init] Heap Region Size: Disabled 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: DisabledCopy the code

This is running on Linux, and we can see that Large Page support is enabled. Explicit means that a HugeTLB page is used. Running with the -xx :+UseTransparentHugePages log line would look like this:

[0.030s][info][gc,init] Large Page Support: Enabled (Transparent)
Copy the code

The above shows only whether large Pages are enabled or not. If you want to learn more about using the Large Pages JVM, you can enable -xlog :pagesize and get the following output:

[s] 0.002 [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=64MCopy the code

This is very detailed information, and it’s a good way to verify which parts of the JVM are supported by Large Pages. The output above, which was generated using JDK 16, has a bug that causes CodeHeap pages to be incorrectly sized, and they are also supported by Large Pages.

V. The translator said

This article is a precursor to another Java17 GC article, and we’ve already published a number of Java17 related articles, with a number more in the pipeline.

  • Shenandoah in OpenJDK 17: Submillisecond GC pauses

In addition, the author’s open source microservice component micA has been adapted to Java17. Pay attention to me and learn technology without getting lost!