eclipse / omr

Eclipse OMR™ Cross platform components for building reliable, high performance language runtimes
http://www.eclipse.org/omr
Other
934 stars 392 forks source link

Generic Double Memory Map API #7396

Open dsouzai opened 1 week ago

dsouzai commented 1 week ago

This topic technically spans both OpenJ9 and OMR, the use case is in OpenJ9 but the problem I'm running into is on the OMR side, so I'm opening this here. This issue is Linux specific.

Background

As part of the Debug on Restore work, I'm working on reducing the footprint increase that comes from generating FSD code pre-checkpoint. A relatively straightforward approach is to try and disclaim the code at checkpoint, so on restore the RSS goes down (on restore, by default, the JVM won't be running FSD code anymore).

There already exists infrastructure to use mmap to allocate memory backed by a temporary file; the segment type needs to have the MEMORY_TYPE_DISCLAIMABLE_TO_FILE flag set. This is currently what happens if the -Xjit:enableCodeCacheDisclaiming option is enabled. The problem though is that unless the filesystem supports it, the entire code cache stops being THP eligible, which has an impact on throughput.

I started doing experiments with mmap and using it to double map, and found that it is actually possible to get the best of both worlds, namely disclaimable memory by having it backed by a file, and anonymous memory that is THP eligible. If I use mmap to allocate a chunk of memory with

  int prot = PROT_EXEC | PROT_READ | PROT_WRITE;
  int flags = MAP_ANONYMOUS | MAP_PRIVATE;
  int fd = -1;

and then I use mmap again on a sub-region with

  int prot = PROT_EXEC | PROT_READ | PROT_WRITE;
  int flags_for_remap = MAP_SHARED | MAP_FIXED;
  int fd = <fd of a temp file created with mkostemp>; // same mechanism as in omr/port/linux/omrvmem.c

I can create regions within the anonymous memory which are backed by a file. The anonymous regions remain THP eligible while the (now) file-backed regions can be disclaimed with madvise.

Potential Implementation

I'm trying to find a way to implement the necessary double map in a clean way. I started to create a function in OpenJ9

void * mapVirtualMemoryInSegmentListToTemporaryFile(J9JavaVM *javaVM, J9MemorySegmentList *segmentList, void *address, UDATA size, UDATA type, J9PortVmemParams *vmemParams)

that would take an address and size, ensure that the range is a valid allocation within segmentList, and then call some OMR API to double map. This part does not seem problematic. However, on the OMR side, it would be nice to reuse the code in

reserve_memory_with_mmap(struct OMRPortLibrary *portLibrary, void *address, uintptr_t byteAmount, struct J9PortVmemIdentifier *identifier, uintptr_t mode, uintptr_t pageSize, OMRMemCategory *category)

with maybe something like

// omrport.h
#define OMRPORT_VMEM_MEMORY_MODE_REMAP 0x000002000
...
// port/linux/omrvmem.c
        /* Do NOT use the MAP_FIXED flag on Linux unless OMRPORT_VMEM_MEMORY_MODE_REMAP
         * is set; with this flag, Linux may return an address that has already been reserved.
         */
        if (OMR_ARE_ANY_BITS_SET(mode, OMRPORT_VMEM_MEMORY_MODE_REMAP)) {
            flags |= MAP_FIXED;
        }

to account for the fact hat MAP_FIXED is needed.

Problem

The problem comes from the fact that reserve_memory_with_mmap calls update_vmemIdentifier. This would incorrectly change information of the original vmem allocation. On the other hand, it would be good to know about the double mapped sub ranges.

So my question is, what is the best way of approaching this?

dsouzai commented 1 week ago

fyi @dmitripivkine @mpirvu (feel free to tag anyone else you think should be part of this discussion).