Skip to content

Commit 372595c

Browse files
committed
8256390: ZGC: Relocate in-place instead of having a heap reserve
Reviewed-by: stefank, eosterlund
1 parent 1df94c9 commit 372595c

35 files changed

+935
-686
lines changed

src/hotspot/share/gc/z/zAllocationFlags.hpp

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,72 +31,50 @@
3131
// Allocation flags layout
3232
// -----------------------
3333
//
34-
// 7 4 3 2 1 0
35-
// +---+-+-+-+-+-+
36-
// |000|1|1|1|1|1|
37-
// +---+-+-+-+-+-+
38-
// | | | | | |
39-
// | | | | | * 0-0 Worker Thread Flag (1-bit)
40-
// | | | | |
41-
// | | | | * 1-1 Non-Blocking Flag (1-bit)
42-
// | | | |
43-
// | | | * 2-2 Relocation Flag (1-bit)
44-
// | | |
45-
// | | * 3-3 No Reserve Flag (1-bit)
46-
// | |
47-
// | * 4-4 Low Address Flag (1-bit)
34+
// 7 2 1 0
35+
// +-----+-+-+-+
36+
// |00000|1|1|1|
37+
// +-----+-+-+-+
38+
// | | | |
39+
// | | | * 0-0 Non-Blocking Flag (1-bit)
40+
// | | |
41+
// | | * 1-1 Worker Relocation Flag (1-bit)
42+
// | |
43+
// | * 2-2 Low Address Flag (1-bit)
4844
// |
49-
// * 7-5 Unused (3-bits)
45+
// * 7-3 Unused (5-bits)
5046
//
5147

5248
class ZAllocationFlags {
5349
private:
54-
typedef ZBitField<uint8_t, bool, 0, 1> field_worker_thread;
55-
typedef ZBitField<uint8_t, bool, 1, 1> field_non_blocking;
56-
typedef ZBitField<uint8_t, bool, 2, 1> field_relocation;
57-
typedef ZBitField<uint8_t, bool, 3, 1> field_no_reserve;
58-
typedef ZBitField<uint8_t, bool, 4, 1> field_low_address;
50+
typedef ZBitField<uint8_t, bool, 0, 1> field_non_blocking;
51+
typedef ZBitField<uint8_t, bool, 1, 1> field_worker_relocation;
52+
typedef ZBitField<uint8_t, bool, 2, 1> field_low_address;
5953

6054
uint8_t _flags;
6155

6256
public:
6357
ZAllocationFlags() :
6458
_flags(0) {}
6559

66-
void set_worker_thread() {
67-
_flags |= field_worker_thread::encode(true);
68-
}
69-
7060
void set_non_blocking() {
7161
_flags |= field_non_blocking::encode(true);
7262
}
7363

74-
void set_relocation() {
75-
_flags |= field_relocation::encode(true);
76-
}
77-
78-
void set_no_reserve() {
79-
_flags |= field_no_reserve::encode(true);
64+
void set_worker_relocation() {
65+
_flags |= field_worker_relocation::encode(true);
8066
}
8167

8268
void set_low_address() {
8369
_flags |= field_low_address::encode(true);
8470
}
8571

86-
bool worker_thread() const {
87-
return field_worker_thread::decode(_flags);
88-
}
89-
9072
bool non_blocking() const {
9173
return field_non_blocking::decode(_flags);
9274
}
9375

94-
bool relocation() const {
95-
return field_relocation::decode(_flags);
96-
}
97-
98-
bool no_reserve() const {
99-
return field_no_reserve::decode(_flags);
76+
bool worker_relocation() const {
77+
return field_worker_relocation::decode(_flags);
10078
}
10179

10280
bool low_address() const {

src/hotspot/share/gc/z/zArguments.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,6 @@ void ZArguments::initialize() {
7171
vm_exit_during_initialization("The flag -XX:+UseZGC can not be combined with -XX:ConcGCThreads=0");
7272
}
7373

74-
// Select medium page size so that we can calculate the max reserve
75-
ZHeuristics::set_medium_page_size();
76-
77-
// MinHeapSize/InitialHeapSize must be at least as large as the max reserve
78-
const size_t max_reserve = ZHeuristics::max_reserve();
79-
if (MinHeapSize < max_reserve) {
80-
FLAG_SET_ERGO(MinHeapSize, max_reserve);
81-
}
82-
if (InitialHeapSize < max_reserve) {
83-
FLAG_SET_ERGO(InitialHeapSize, max_reserve);
84-
}
85-
8674
#ifdef COMPILER2
8775
// Enable loop strip mining by default
8876
if (FLAG_IS_DEFAULT(UseCountedLoopSafepoints)) {

src/hotspot/share/gc/z/zDirector.cpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@
2525
#include "gc/z/zCollectedHeap.hpp"
2626
#include "gc/z/zDirector.hpp"
2727
#include "gc/z/zHeap.inline.hpp"
28+
#include "gc/z/zHeuristics.hpp"
2829
#include "gc/z/zStat.hpp"
2930
#include "logging/log.hpp"
3031

3132
const double ZDirector::one_in_1000 = 3.290527;
3233

3334
ZDirector::ZDirector() :
35+
_relocation_headroom(ZHeuristics::relocation_headroom()),
3436
_metronome(ZStatAllocRate::sample_hz) {
3537
set_name("ZDirector");
3638
create_and_start();
@@ -95,14 +97,12 @@ bool ZDirector::rule_allocation_rate() const {
9597
// margin based on variations in the allocation rate and unforeseen
9698
// allocation spikes.
9799

98-
// Calculate amount of free memory available to Java threads. Note that
99-
// the heap reserve is not available to Java threads and is therefore not
100-
// considered part of the free memory.
100+
// Calculate amount of free memory available. Note that we take the
101+
// relocation headroom into account to avoid in-place relocation.
101102
const size_t soft_max_capacity = ZHeap::heap()->soft_max_capacity();
102-
const size_t max_reserve = ZHeap::heap()->max_reserve();
103103
const size_t used = ZHeap::heap()->used();
104-
const size_t free_with_reserve = soft_max_capacity - MIN2(soft_max_capacity, used);
105-
const size_t free = free_with_reserve - MIN2(free_with_reserve, max_reserve);
104+
const size_t free_including_headroom = soft_max_capacity - MIN2(soft_max_capacity, used);
105+
const size_t free = free_including_headroom - MIN2(free_including_headroom, _relocation_headroom);
106106

107107
// Calculate time until OOM given the max allocation rate and the amount
108108
// of free memory. The allocation rate is a moving average and we multiply
@@ -179,14 +179,12 @@ bool ZDirector::rule_high_usage() const {
179179
// memory is still slowly but surely heading towards zero. In this situation,
180180
// we start a GC cycle to avoid a potential allocation stall later.
181181

182-
// Calculate amount of free memory available to Java threads. Note that
183-
// the heap reserve is not available to Java threads and is therefore not
184-
// considered part of the free memory.
182+
// Calculate amount of free memory available. Note that we take the
183+
// relocation headroom into account to avoid in-place relocation.
185184
const size_t soft_max_capacity = ZHeap::heap()->soft_max_capacity();
186-
const size_t max_reserve = ZHeap::heap()->max_reserve();
187185
const size_t used = ZHeap::heap()->used();
188-
const size_t free_with_reserve = soft_max_capacity - MIN2(soft_max_capacity, used);
189-
const size_t free = free_with_reserve - MIN2(free_with_reserve, max_reserve);
186+
const size_t free_including_headroom = soft_max_capacity - MIN2(soft_max_capacity, used);
187+
const size_t free = free_including_headroom - MIN2(free_including_headroom, _relocation_headroom);
190188
const double free_percent = percent_of(free, soft_max_capacity);
191189

192190
log_debug(gc, director)("Rule: High Usage, Free: " SIZE_FORMAT "MB(%.1f%%)",

src/hotspot/share/gc/z/zDirector.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -32,7 +32,8 @@ class ZDirector : public ConcurrentGCThread {
3232
private:
3333
static const double one_in_1000;
3434

35-
ZMetronome _metronome;
35+
const size_t _relocation_headroom;
36+
ZMetronome _metronome;
3637

3738
void sample_allocation_rate() const;
3839

src/hotspot/share/gc/z/zForwarding.cpp

Lines changed: 139 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,144 @@
2222
*/
2323

2424
#include "precompiled.hpp"
25+
#include "gc/z/zAddress.inline.hpp"
2526
#include "gc/z/zForwarding.inline.hpp"
26-
#include "utilities/debug.hpp"
27+
#include "gc/z/zStat.hpp"
28+
#include "gc/z/zUtils.inline.hpp"
29+
#include "utilities/align.hpp"
30+
31+
//
32+
// Reference count states:
33+
//
34+
// * If the reference count is zero, it will never change again.
35+
//
36+
// * If the reference count is positive, it can be both retained
37+
// (increased) and released (decreased).
38+
//
39+
// * If the reference count is negative, is can only be released
40+
// (increased). A negative reference count means that one or more
41+
// threads are waiting for one or more other threads to release
42+
// their references.
43+
//
44+
// The reference lock is used for waiting until the reference
45+
// count has become zero (released) or negative one (claimed).
46+
//
47+
48+
static const ZStatCriticalPhase ZCriticalPhaseRelocationStall("Relocation Stall");
49+
50+
bool ZForwarding::retain_page() {
51+
for (;;) {
52+
const int32_t ref_count = Atomic::load_acquire(&_ref_count);
53+
54+
if (ref_count == 0) {
55+
// Released
56+
return false;
57+
}
58+
59+
if (ref_count < 0) {
60+
// Claimed
61+
wait_page_released();
62+
return false;
63+
}
64+
65+
if (Atomic::cmpxchg(&_ref_count, ref_count, ref_count + 1) == ref_count) {
66+
// Retained
67+
return true;
68+
}
69+
}
70+
}
71+
72+
ZPage* ZForwarding::claim_page() {
73+
for (;;) {
74+
const int32_t ref_count = Atomic::load(&_ref_count);
75+
assert(ref_count > 0, "Invalid state");
76+
77+
// Invert reference count
78+
if (Atomic::cmpxchg(&_ref_count, ref_count, -ref_count) != ref_count) {
79+
continue;
80+
}
81+
82+
// If the previous reference count was 1, then we just changed it to -1,
83+
// and we have now claimed the page. Otherwise we wait until it is claimed.
84+
if (ref_count != 1) {
85+
ZLocker<ZConditionLock> locker(&_ref_lock);
86+
while (Atomic::load_acquire(&_ref_count) != -1) {
87+
_ref_lock.wait();
88+
}
89+
}
90+
91+
return _page;
92+
}
93+
}
94+
95+
void ZForwarding::release_page() {
96+
for (;;) {
97+
const int32_t ref_count = Atomic::load(&_ref_count);
98+
assert(ref_count != 0, "Invalid state");
99+
100+
if (ref_count > 0) {
101+
// Decrement reference count
102+
if (Atomic::cmpxchg(&_ref_count, ref_count, ref_count - 1) != ref_count) {
103+
continue;
104+
}
105+
106+
// If the previous reference count was 1, then we just decremented
107+
// it to 0 and we should signal that the page is now released.
108+
if (ref_count == 1) {
109+
// Notify released
110+
ZLocker<ZConditionLock> locker(&_ref_lock);
111+
_ref_lock.notify_all();
112+
}
113+
} else {
114+
// Increment reference count
115+
if (Atomic::cmpxchg(&_ref_count, ref_count, ref_count + 1) != ref_count) {
116+
continue;
117+
}
118+
119+
// If the previous reference count was -2 or -1, then we just incremented it
120+
// to -1 or 0, and we should signal the that page is now claimed or released.
121+
if (ref_count == -2 || ref_count == -1) {
122+
// Notify claimed or released
123+
ZLocker<ZConditionLock> locker(&_ref_lock);
124+
_ref_lock.notify_all();
125+
}
126+
}
127+
128+
return;
129+
}
130+
}
131+
132+
void ZForwarding::wait_page_released() const {
133+
if (Atomic::load_acquire(&_ref_count) != 0) {
134+
ZStatTimer timer(ZCriticalPhaseRelocationStall);
135+
ZLocker<ZConditionLock> locker(&_ref_lock);
136+
while (Atomic::load_acquire(&_ref_count) != 0) {
137+
_ref_lock.wait();
138+
}
139+
}
140+
}
141+
142+
ZPage* ZForwarding::detach_page() {
143+
// Wait until released
144+
if (Atomic::load_acquire(&_ref_count) != 0) {
145+
ZLocker<ZConditionLock> locker(&_ref_lock);
146+
while (Atomic::load_acquire(&_ref_count) != 0) {
147+
_ref_lock.wait();
148+
}
149+
}
150+
151+
// Detach and return page
152+
ZPage* const page = _page;
153+
_page = NULL;
154+
return page;
155+
}
27156

28157
void ZForwarding::verify() const {
29-
guarantee(_refcount > 0, "Invalid refcount");
158+
guarantee(_ref_count != 0, "Invalid reference count");
30159
guarantee(_page != NULL, "Invalid page");
31160

32-
size_t live_objects = 0;
161+
uint32_t live_objects = 0;
162+
size_t live_bytes = 0;
33163

34164
for (ZForwardingCursor i = 0; i < _entries.length(); i++) {
35165
const ZForwardingEntry entry = at(&i);
@@ -53,9 +183,13 @@ void ZForwarding::verify() const {
53183
guarantee(entry.to_offset() != other.to_offset(), "Duplicate to");
54184
}
55185

186+
const uintptr_t to_addr = ZAddress::good(entry.to_offset());
187+
const size_t size = ZUtils::object_size(to_addr);
188+
const size_t aligned_size = align_up(size, _page->object_alignment());
189+
live_bytes += aligned_size;
56190
live_objects++;
57191
}
58192

59-
// Check number of non-empty entries
60-
guarantee(live_objects == _page->live_objects(), "Invalid number of entries");
193+
// Verify number of live objects and bytes
194+
_page->verify_live(live_objects, live_bytes);
61195
}

0 commit comments

Comments
 (0)