hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp
//
// This method is pretty bulky. It would be nice to split it up
// into smaller submethods, but we need to be careful not to hurt
// performance.
//
oop PSPromotionManager::copy_to_survivor_space(oop o) {
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(PSScavenge::should_scavenge(&o), "Sanity");
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
oop new_obj = NULL;
{- -------------------------------------------
(1) (変数宣言など)
(オブジェクトの mark フィールドは, GC 中は複数のスレッドから同時にアクセスされうるので
oop のメソッドを使ってアクセスしないように, とのこと)
---------------------------------------- -}
// NOTE! We must be very careful with any methods that access the mark
// in o. There may be multiple threads racing on it, and it may be forwarded
// at any time. Do not use oop methods for accessing the mark!
markOop test_mark = o->mark();
{- -------------------------------------------
(1) もし処理対象のオブジェクトが未だコピーされていなければ,
以下のブロック内(if の then 側)の処理を行う.
---------------------------------------- -}
// The same test as "o->is_forwarded()"
if (!test_mark->is_marked()) {
{- -------------------------------------------
(1.1) (変数宣言など)
---------------------------------------- -}
bool new_obj_is_tenured = false;
size_t new_obj_size = o->size();
{- -------------------------------------------
(1.1) MT safe な方法でオブジェクトの年齢を取得する.
---------------------------------------- -}
// Find the objects age, MT safe.
int age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ?
test_mark->displaced_mark_helper()->age() : test_mark->age();
{- -------------------------------------------
(1.1) オブジェクトの年齢が Old 領域への昇格閾値(= PSScavenge::tenuring_threshold())より低い場合には,
PSYoungPromotionLAB::allocate() で To 領域内にコピー先を確保する.
なお, PSYoungPromotionLAB::allocate() が失敗した場合,
まだ To 領域に空きがあれば以下の処理を行う.
* 処理対象のオブジェクトの大きさが YoungPLABSize/2 を超えているなら,
MutableSpace::cas_allocate() (または MutableNUMASpace::cas_allocate()) で
直接 To 領域内に確保を試みる.
* そうでなければ, To 領域から新しい PSYoungPromotionLAB を確保する.
(PSPromotionLAB::flush() で現在使っている PSYoungPromotionLab の残りのスペースを dummy 配列で埋めた後,
MutableSpace::cas_allocate() (または MutableNUMASpace::cas_allocate()) で新しい PSYoungPromotionLab 用の領域を確保する.
確保が成功すれば, 改めて PSYoungPromotionLAB::allocate() により To 領域内にコピー先を確保する.
なお新しい PSYoungPromotionLab の確保に失敗した場合には, 今後も新たな PSYoungPromotionLab 用の領域は確保できないので,
無駄に確保を試みないように _young_gen_is_full フィールドを true にしている.)
---------------------------------------- -}
// Try allocating obj in to-space (unless too old)
if (age < PSScavenge::tenuring_threshold()) {
new_obj = (oop) _young_lab.allocate(new_obj_size);
if (new_obj == NULL && !_young_gen_is_full) {
// Do we allocate directly, or flush and refill?
if (new_obj_size > (YoungPLABSize / 2)) {
// Allocate this object directly
new_obj = (oop)young_space()->cas_allocate(new_obj_size);
} else {
// Flush and fill
_young_lab.flush();
HeapWord* lab_base = young_space()->cas_allocate(YoungPLABSize);
if (lab_base != NULL) {
_young_lab.initialize(MemRegion(lab_base, YoungPLABSize));
// Try the young lab allocation again.
new_obj = (oop) _young_lab.allocate(new_obj_size);
} else {
_young_gen_is_full = true;
}
}
}
}
{- -------------------------------------------
(1.1) オブジェクトの年齢が Old 領域への昇格閾値より高い場合,
もしくは To 領域内にメモリを確保するのに失敗した場合には,
PSOldPromotionLAB::allocate() で Old 領域内にコピー先を確保する.
なお, PSOldPromotionLAB::allocate() が失敗した場合,
まだ Old 領域に空きがあれば以下の処理を行う.
* 処理対象のオブジェクトの大きさが OldPLABSize/2 を超えているなら,
PSOldGen::cas_allocate() で 直接 Old 領域内に確保を試みる.
* そうでなければ, Old 領域から新しい PSOldPromotionLAB を確保する.
(PSOldPromotionLAB::flush() で現在使っている PSOldPromotionLAB の残りのスペースを dummy 配列で埋めた後,
PSOldGen::cas_allocate() で新しい PSOldPromotionLAB 用の領域を確保する.
確保が成功すれば, 改めて PSOldPromotionLAB::allocate() により Old 領域内にコピー先を確保する.
以上の処理を行ってもコピー先の確保ができなかった場合には,
PSPromotionManager::oop_promotion_failed() で失敗したことを記録した後,
オブジェクト内のポインタを PSPromotionManager のタスクキューに追加して, ここでリターン.
(ただし, この場合でも並列で動作している他のスレッドがコピーを成功させていた場合には,
確保が失敗したという記録は行わずそのままリターンする.)
(なおこの確保失敗のケースでは, 後で PSScavenge::clean_up_failed_promotion() により後始末が行われる.
See: PSScavenge::clean_up_failed_promotion())
---------------------------------------- -}
// Otherwise try allocating obj tenured
if (new_obj == NULL) {
#ifndef PRODUCT
if (Universe::heap()->promotion_should_fail()) {
return oop_promotion_failed(o, test_mark);
}
#endif // #ifndef PRODUCT
new_obj = (oop) _old_lab.allocate(new_obj_size);
new_obj_is_tenured = true;
if (new_obj == NULL) {
if (!_old_gen_is_full) {
// Do we allocate directly, or flush and refill?
if (new_obj_size > (OldPLABSize / 2)) {
// Allocate this object directly
new_obj = (oop)old_gen()->cas_allocate(new_obj_size);
} else {
// Flush and fill
_old_lab.flush();
HeapWord* lab_base = old_gen()->cas_allocate(OldPLABSize);
if(lab_base != NULL) {
_old_lab.initialize(MemRegion(lab_base, OldPLABSize));
// Try the old lab allocation again.
new_obj = (oop) _old_lab.allocate(new_obj_size);
}
}
}
// This is the promotion failed test, and code handling.
// The code belongs here for two reasons. It is slightly
// different thatn the code below, and cannot share the
// CAS testing code. Keeping the code here also minimizes
// the impact on the common case fast path code.
if (new_obj == NULL) {
_old_gen_is_full = true;
return oop_promotion_failed(o, test_mark);
}
}
}
{- -------------------------------------------
(1.1) (assert)
---------------------------------------- -}
assert(new_obj != NULL, "allocation should have succeeded");
{- -------------------------------------------
(1.1) オブジェクトの中身をコピーの領域にコピーする
---------------------------------------- -}
// Copy obj
Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size);
{- -------------------------------------------
(1.1) コピーが終わったので, コピー元のオブジェクトの mark フィールドに
CAS で forwarding pointer(コピー先のメモリアドレス) を埋め込む.
---------------------------------------- -}
// Now we have to CAS in the header.
if (o->cas_forward_to(new_obj, test_mark)) {
{- -------------------------------------------
(1.1) CAS が成功した場合は, (コピー先が Old 領域でなければ oopDesc::incr_age() でオブジェクトの年齢を増加させた後, )
オブジェクト内のポインタを PSPromotionManager オブジェクト内に回収する.
* コピーしたのが非常に大きい配列だった場合は,
mask_chunked_array_oop() で配列だと分かるように印を付けた後
PSPromotionManager::push_depth() でタスクキューに追加する.
(より正確には, PSChunkLargeArrays オプションが指定されており, かつ
大きさが 1.5*ParGCArrayScanChunk よりも大きい配列オブジェクトの場合)
* そうでなければ,
oopDesc::push_contents() でオブジェクト内のポインタをタスクキューに追加する.
---------------------------------------- -}
// We won any races, we "own" this object.
assert(new_obj == o->forwardee(), "Sanity");
// Increment age if obj still in new generation. Now that
// we're dealing with a markOop that cannot change, it is
// okay to use the non mt safe oop methods.
if (!new_obj_is_tenured) {
new_obj->incr_age();
assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj");
}
// Do the size comparison first with new_obj_size, which we
// already have. Hopefully, only a few objects are larger than
// _min_array_size_for_chunking, and most of them will be arrays.
// So, the is->objArray() test would be very infrequent.
if (new_obj_size > _min_array_size_for_chunking &&
new_obj->is_objArray() &&
PSChunkLargeArrays) {
// we'll chunk it
oop* const masked_o = mask_chunked_array_oop(o);
push_depth(masked_o);
TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes);
} else {
// we'll just push its contents
new_obj->push_contents(this);
}
{- -------------------------------------------
(1.1) CAS が失敗した場合は, コピー先として確保していたメモリを PSPromotionLAB::unallocate_object() で deallocate する.
(この場合は, 他の誰かがコピーしたということなので, forwardee() でコピー先を取得しておく.)
(なお, 直接確保してしまっていた場合は deallocate できない.
deallocate に失敗したら, しょうが無いので
CollectedHeap::fill_with_object() でコピー先のメモリ領域を dummy 配列で埋めるだけにしておく.)
---------------------------------------- -}
} else {
// We lost, someone else "owns" this object
guarantee(o->is_forwarded(), "Object must be forwarded if the cas failed.");
// Try to deallocate the space. If it was directly allocated we cannot
// deallocate it, so we have to test. If the deallocation fails,
// overwrite with a filler object.
if (new_obj_is_tenured) {
if (!_old_lab.unallocate_object(new_obj)) {
CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size);
}
} else if (!_young_lab.unallocate_object(new_obj)) {
CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size);
}
// don't update this before the unallocation!
new_obj = o->forwardee();
}
{- -------------------------------------------
(1) もし処理対象のオブジェクトが既にコピー済みであれば,
単にコピー先のアドレスを取ってくるだけ.
---------------------------------------- -}
} else {
assert(o->is_forwarded(), "Sanity");
new_obj = o->forwardee();
}
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
#ifdef DEBUG
// This code must come after the CAS test, or it will print incorrect
// information.
if (TraceScavenge) {
gclog_or_tty->print_cr("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (" SIZE_FORMAT ")}",
PSScavenge::should_scavenge(&new_obj) ? "copying" : "tenuring",
new_obj->blueprint()->internal_name(), o, new_obj, new_obj->size());
}
#endif
{- -------------------------------------------
(1) 結果をリターン
---------------------------------------- -}
return new_obj;
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.