Java HotSpot VM Serial Collector
java gc
Serial Collector
멀티 프로세서에서는 비효율적인 GC이지만, 100MB 정도로 작은 규모의 데이터셋을 사용하는 애플리케이션이라면 멀티 프로세서에서도 쓸만하다. Serial Collector는 VM이 운영체제와 하드웨어 환경에 따라 자동으로 선택하거나, -XX:+UseSerialGC
옵션으로 활성화할 수 있다.
Native 코드를 읽어보자
- OpenJDK의 JVM C++ 코드를 읽으면서 내가 알고 있는 사실들을 확인해 보자.
- 코드 출처는 @ 55739:7e2238451585 jdk-13+24
- 읽다보면 의외의 깨달음을 얻게 될지도 모른다.
eden과 survivor의 max size 계산
_eden_space = new ContiguousSpace();
_from_space = new ContiguousSpace(); /* survivor (from 역할) */
_to_space = new ContiguousSpace(); /* survivor (to 역할) */
if (_eden_space == NULL || _from_space == NULL || _to_space == NULL) {
vm_exit_during_initialization("Could not allocate a new gen space");
}
// Compute the maximum eden and survivor space sizes. These sizes
// are computed assuming the entire reserved space is committed.
// These values are exported as performance counters.
uintx size = _virtual_space.reserved_size();
_max_survivor_size = compute_survivor_size(size, SpaceAlignment);
_max_eden_size = size - (2*_max_survivor_size); /* eden max 사이즈 계산 */
// allocate the performance counters
// Generation counters -- generation 0, 3 subspaces
_gen_counters = new GenerationCounters("new", 0, 3,
min_size, max_size, &_virtual_space);
_gc_counters = new CollectorCounters(policy, 0);
_eden_counters = new CSpaceCounters(
"eden", 0, _max_eden_size, _eden_space,
_gen_counters);
_from_counters = new CSpaceCounters("s0", 1, _max_survivor_size, _from_space,
_gen_counters);
_to_counters = new CSpaceCounters("s1", 2, _max_survivor_size, _to_space,
_gen_counters);
- survivor 두 개(s0, s1)는 같은 max 크기를 가진다.
- eden 사이즈는 할당된 young 영역의 공간에서 두 개의 survivor size를 뺀 값으로 계산된다.
두 Survivor의 스왑
void DefNewGeneration::swap_spaces
void DefNewGeneration::swap_spaces() {
/* from 과 to 를 swap 한다. */
ContiguousSpace* s = from();
_from_space = to();
_to_space = s;
eden()->set_next_compaction_space(from());
// The to-space is normally empty before a compaction so need
// not be considered. The exception is during promotion
// failure handling when to-space can contain live objects.
from()->set_next_compaction_space(NULL);
if (UsePerfData) {
CSpaceCounters* c = _from_counters;
_from_counters = _to_counters;
_to_counters = c;
}
}
Aging
oop DefNewGeneration::copy_to_survivor_space
oop DefNewGeneration::copy_to_survivor_space(oop old) {
assert(is_in_reserved(old) && !old->is_forwarded(),
"shouldn't be scavenging this oop");
size_t s = old->size(); /* old 객체의 사이즈 */
oop obj = NULL;
// Try allocating obj in to-space (unless too old)
if (old->age() < tenuring_threshold()) {
/* old 객체의 age가 임계값 미만이면 survivor(to)영역에 객체의 크기만한 공간을 마련한다 */
obj = (oop) to()->allocate_aligned(s);
}
// Otherwise try allocating obj tenured
if (obj == NULL) {
/* 공간이 마련되지 않았다면 old 영역으로 보내버린다(promotion) */
obj = _old_gen->promote(old, s);
if (obj == NULL) {
handle_promotion_failure(old);
return old;
}
} else {
/* 공간이 마련됐다면 survivor(to) 영역으로 보낸다 */
// Prefetch beyond obj
const intx interval = PrefetchCopyIntervalInBytes;
Prefetch::write(obj, interval);
// Copy obj
Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)obj, s);
// Increment age if obj still in new generation
/* 아직 new gen에 있으므로 객체는 나이를 먹는다 */
obj->incr_age();
age_table()->add(obj, s);
}
// Done, insert forward pointer to obj in this header
old->forward_to(obj);
return obj;
}
License
- 이 글에 포함된 OpenJDK 코드는 "The GNU General Public License (GPL)" 라이선스를 따릅니다.
- 이 글에 포함된 OpenJDK 코드에 대해 들여쓰기, 줄바꿈 등을 수정하였으며 한국어가 포함된 주석은 제가 작성한 것입니다.