Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Metaspace is a native (as in, : off-heap) memory manager in the hotspot.

...

With compressed class space enabled, we have two Metaspace contexts (one normal, one wrapping the class space), and each CLD has now two arenas, one associated with non-class context, one with the class space.

In this mode, memory for Klass structures is allocated from the class space, all other metaspace allocations from the non-class metaspace:

        +--------+              +--------+
        |  CLD   |              |  CLD   |
        +--------+              +--------+
         /     \                 /     \          Each CLD has two arenas...             
        /       \               /       \       
       /         \             /         \      
      v           v           v           v             
  +--------+  +--------+  +--------+  +--------+
  | noncl  |  | class  |  | noncl  |  | class  |
  | arena  |  | arena  |  | arena  |  | arena  |
  +--------+  +--------+  +--------+  +--------+
      |              \      /            |       
      |               --------\          |        Non-class arenas take from non-class context,
      |                   /   |          |        class arenas take from class context
      |         /---------    |          |       
      v         v             v          v  
  +--------------------+  +------------------------+
  |                    |  |                        |
  | Metaspace Context  |  | Metaspace Context      |
  |     (nonclass)     |  |     (class)            |
  |                    |  |                        |
  +--------------------+  +------------------------+
         |            |            |
         |            |            |                    Non-class context: list of smallish mappings
         |            |            |                    Class context: one large mapping (the class space)
         v            v            v
  +--------+  +--------+  +----------------~~~~~~~-----+
  |        |  |        |  |                            |
  | virtual|  | virt   |  | virt space (class space)   |
  | space  |  | space  |  |                            |
  |        |  |        |  |                            |
  +--------+  +--------+  +----------------~~~~~~~-----+

...

+-------------------+-------------------+ 
| Leader            |         |    |    |
+-------------------+-------------------+

Merging chunks


A free chunk can be merged with its buddy if that buddy is free and unsplit. This is done recursively, until either one of the partners in the chunk pair are not free and unsplit, or until the largest chunk size - root chunk size - is reached.

...

+---------+---------+-------------------+
|    A    |    b    |         C         |
+---------+---------+-------------------+
                    |
                    v
+-------------------+-------------------+
|         A`        |         C         |
+-------------------+-------------------+
                    |
                    v
+-------------------+-------------------+
|                  A``                  |
+-------------------+-------------------+

Splitting chunks

To get a small chunk from a larger chunk, a large chunk can be split. Splitting happens in power-of-2 sizes. A split operation yields the desired smaller chunk as well as 1-n splinter chunks.

...

Step
     +---------------------------------------+
 0   |                   A                   |
     +---------------------------------------+

                         |
                         v
     +-------------------+-------------------+
 1   | d1 | D2 |    C    |         B         |
     +-------------------+-------------------+
       ^
       Result chunk
                         |
                         v
     +-------------------+-------------------+
 2   | d1 | d2 |    c    |         B         |
     +-------------------+-------------------+
            ^
            Result chunk
                         |
                         v
     +-------------------+-------------------+
 3   | d1 | d2 | c1 | C2 |         B         |
     +-------------------+-------------------+
                 ^
                 Result chunk


How it all looks in memory


Allocated metaspace blocks (the user-level unit of allocation) reside in chunks; chunks reside in mappings called VirtualSpaceNode, of which multiple may exist:

...

 +------------------+ <--- virtual memory region
 | +-------------+  | <--- chunk
 | | +---------+ |  | <--- block 
 | | |         | |  |
 | | +---------+ |  |
 | | +---------+ |  | <--- block 
 | | |         | |  |
 | | |         | |  |
 | | |         | |  |
 | | +---------+ |  |
 | | +---------+ |  | <--- block 
 | | +---------+ |  |
 | |             |  |
 | +-------------+  | <--- end: chunk
 | +-------------+  | <--- chunk
 | | +---------+ |  |
 | | |         | |  |
        ...
 +------------------+ <--- end: virtual memory region
  
  
 +------------------+ <--- next virtual memory region
 | +-------------+  |
 | | +---------+ |  |
 | | |         | |  |
 | | +---------+ |  |
       ...


Subsystems

Metaspace implementation is divided into separate sub systems, each of which is isolated from its peers and has a small number of tasks.

Image Added

The Virtual Memory Subsystem

Image Added

Classes:

- VirtualSpaceList

- VirtualSpaceNode

- RootChunkArea and RootChunkAreaLUT

- CommitMask

- CommitLimiter

The Virtual Memory Layer is the lowest subsystem. It forms one half of a metaspace context (the upper half being the chunk manager).

It is responsible for reserving and committing memory. It knows about commit granules. Its outside interface to upper layers is the VirtualSpaceList while some operations are also directly exposed via VirtualSpaceNode.

Essential operations

Other operations

The Virtual Memory Subsystem takes care of Buddy Allocator operations, on behalf of upper regions:

Classes

VirtualSpaceList

VirtualSpaceList (virtualSpaceList.hpp) encapsulates a list of memory mappings (instances of VirtualSpaceNode). This list can be expandable - new mappings can be added on demand - or non-expandable.

The non-expandable latter case is used to represent the class space, which has to be a single contiguous address range for compressed Klass* pointer encoding to work. In that case, the class-space VirtualSpaceList just contains a single node, which wraps the whole of the class space. This may sound complicated but is just a matter of code reuse.

VirtualSpaceNode

VirtualSpaceNode (virtualSpaceNode.hpp) manages one contiguous memory mapping for Metaspace. In case of the compressed class space, this encompasses the whole pre-reserved address range of the class space. In case of the non-class metaspace, these are smallish mappings, by default two root chunks in size.

VirtualSpaceNode knows about commit granules, and it knows which commit granules in it are commited (via the commit mask).

VirtualSpaceNode also knows about root chunks: its memory is divided into a series of root-chunk-sized areas (class RootChunkArea). To keep coding simple, we require each memory mapping to be aligned to root chunk size in both start address and size. 

Note: the concepts of chunks and of commit granules are almost completely independent from each other. The former is a means of handing out/taking back memory while avoiding fragmentation; the latter a way to manage commit state of memory.

Putting this all together, the memory underlying a VirtualSpaceNode looks like this:

base                                                                    end
 |                                                                       |
 v                                                                       v

 | root chunk area | root chunk area | root chunk area | root chunk area |

 |x| |x|x|x| | | | |x|x|x| | | |x|x| | | |x|x|x|x| | | | | |x| |x| | | | |    <-- commit granules (x=committed)
 
CommitMask

(commitMask.hpp

Just a bit mask inside a VirtualSpaceNode holding commit information (one bit per granule).

RootChunkArea and RootChunkAreaLUT

(rootChunkArea.hpp

RootChunkArea contains the buddy allocator code. It is wrapped over the area of a single root chunk.

It knows how to split and merge chunks. It also has a reference to the very first chunk in this area (needed since Metachunk chunk headers are separate entities from their payload, see below, and it is not easy to get from the metaspace start address to its Metachunk).

A RootChunkArea object does not exist on its own but as a part of an array within a VirtualSpaceNode, describing the node's memory.

RootChunkAreaLUT (for "lookup table") just holds the sequence of RootChunkArea classes which cover the memory region of the VirtualSpaceNode.

CommitLimiter

(commitLimiter.hpp

When committing memory for Metaspace, we have to observe two limits:

  • MaxMetaspaceSize (the total cap on the amount of memory we are allowed to commit for metaspace)
  • The GC threshold, which puts a stop-gap into Metaspace growth where, before allowed to grow further, a GC is triggered to attempt class unloading.

(Note: CompressedClassSpaceSize - the reserved size of the class space - is another limit, but it is not explicitly checked in the current implementation simply because it checks itself. If we run out of space in class space we'll notice).

Checking both limits is somewhat complex and also should not be part of a general allocator, therefore limit checking is abstracted away into the class CommitLimiter. Allowing potential future reuse of metaspace coding with different limit logics (and also easier testing).

Under normal circumstances, only one instance of the CommitLimiter ever exists, see CommitLimiter::globalLimiter(), which encapsulates the GC threshold and MaxMetaspace queries.