- Loading...
...
Metaspace is a native (as in, : off-heap) memory manager in the hotspot.
...
Metaspace implementation is divided into separate sub systems, each of which is isolated from its peers and has a small number of tasks.
Classes:
...
"Allocate new root chunk"
Metachunk* VirtualSpaceList::allocate_root_chunk();
This carves out a new root chunk from the underlying reserved space and hands it to the caller (nothing is committed yet, this is purely reserved memory).
"commit this range"
bool VirtualSpaceNode::ensure_range_is_committed(MetaWord* p, size_t word_size);
Upper layers request that a given arbitrary address range should be committed. Subsystem figures out which granules would be affected and makes sure those are committed (which may be a noop if they had been committed before).
When committing, subsystem honors VM limits (MaxMetaspaceSize
resp. the commit gc threshold) via the commit limiter.
"uncommit this range"
void VirtualSpaceNode::uncommit_range(MetaWord* p, size_t word_size);
Similar to committing. Subsystem figures out which commit granules are affected, and uncommits those.
"purge"
void VirtualSpaceList::purge()
This unmaps all completely empty memory regions, and uncommits all unused commit granules.
...
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.
...
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)
Just a bit mask inside a VirtualSpaceNode holding commit information (one bit per granule).
RootChunkArea
contains the buddy allocator code. It is wrapped over the area of a single root chunk.
...
RootChunkAreaLUT
(for "lookup table") just holds the sequence of RootChunkArea
classes which cover the memory region of the VirtualSpaceNode
.
The CommitLimiter contains the limit logic we may want to impose on how much memory can be committed:
In metaspace, we have two limits to committing memory: the absolute limit, MaxMetaspaceSize; and the GC threshold. In both cases an allocation should fail if it would require committing memory and hit one of these limits.
However, the actual Metaspace allocator is a generic one and this GC- and classloading specific logic should be kept separate. Therefore it is hidden inside this interface.
When committing memory for Metaspace, we have to observe two limits:
(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)This allows us to: - more easily write tests for metaspace, by providing a different implementation of the commit limiter, thus keeping test logic separate from VM state. - (potentially) use the metaspace for things other than class metadata, where different commit rules would apply.
Under normal circumstances, only one instance of the CommitLimiter
ever exists, see CommitLimiter::globalLimiter()
, which encapsulates the GC threshold and MaxMetaspace queries.
...