Skip to content

Dynamically select memory range(s) for MMTk's heap #1347

@wks

Description

@wks

Currently, in both Map32 and Map64, we assume MMTk occupies a predefined memory range. The default range for Map32 is 0x8000_0000 to 0xd000_0000, and the default range for Map64 is 0x200_0000_0000 to 0x2200_0000_0000. The generous supply of heap space allows Map64 to implement SFT efficiently, and allows all side metadata to be stored contiguously.

But in real-world applications, MMTk is not free to use the entire address space. The application may arrange its address space for its own specific need, and multiple MMTk instances will compete for the same address range.

I propose we introduce external memory sources to MMTk so that it can request memory from external sources, e.g. the OS via the mmap syscall, or the VM.

Update: Alternatively, MMTk can inspect the /proc/pid/maps and figure out one (or more) proper memory range(s). That is equivalent to calling mmap and letting the OS decide for MMTk.

Note: This is not intended to replace the existing Map32 or Map64, but provide a complement to those.

Existing approaches

CRuby's default GC

CRuby's default GC organizes the heap into 64KB blocks (CRuby calls it "page", but I'll keep using the term "block" to be consistent with MMTk). Each individual block is allocated from mmap or posix_memalign. CRuby maintains both arrays and linked lists of blocks for different purposes. The side metadata are stored in the beginning of each block. (Note that CRuby's max object size is 640B, so a 64K block is almost as big as a chunk for CRuby.)

I am not suggesting that MMTk should externalize the allocation of blocks. It would be too inefficient if we call mmap that often.

OpenJDK

TODO

Suggested interface

trait ExternalChunkResource {
    /// Allocate contiguous range of `num_chunks` chunks.  Must be aligned to chunk.
    fn allocate_contiguous_chunks(num_chunks: usize) -> Option<Address>;
    /// Free an allocated contiguous range of chunks.
    fn free_contiguous_chunks(chunk_group: Address);
}

It should be as simple as that, and that's essentially what Map32 does. mmtk-core will have to maintain its own list/array/vector of acquired "contiguous chunkses", which is currently done by Map32Inner::prev_link and Map32Inner::next_link.

Using this approach, all side metadata will have to be discontiguous, including global metadata. Then "global" will simply mean it exists in every space, but a global side metadata may end up being in each chunk.

We can still have SFT and a chunk-grained Mmapper (or more precisely a MapStateStorage as described in #1345) because the interface demands the chunks are aligned. We can still implement is_mmtk_object using SFT and Mmapper, or alternatively we can do linear or binary search on allocated "contiguous chunkses" to find the chunk an address belongs to.

FreeListPageResource may need to be re-implemented so that it no longer assumes all chunks in all spaces form a contiguous memory range. Alternatively we replace FreeListPageResource with something else.

Alternatives

(Update) As we discussed, there is an important alternative to the above. We dynamically select the memory range at MMTk initialization time (either by looking at /proc/pid/maps or letting an external entity (such as the OS via mmap) decide the range), but we only request one contiguous range for MMTk's heap instead of requesting many discontiguous ranges on demand. This should be easier to implement and gives MMTk more control over the memory range, and makes certain things easier (such as side metadata). As a tradeoff, once the range is determined, it cannot be extended at run time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions