當jvm的eden區滿了,進行回收時,s0區滿了,此時eden區還有存活對象沒複製完,會怎樣?
一直疑問這個問題,當eden區滿了,觸發垃圾回收,此時eden區存活的對象開始複製到s0區,複製過程中s0區滿了,可是eden區存活對象還沒複製完,此時jvm會怎麼辦?
猜測1:eden區沒複製完的對象直接放入old區猜測2:再次觸發整個新生代回收,把eden區存活對象先放入s1,之後再把s0區的存活對象放到s1區,若此過程s1又滿了,而s0還沒複製完,則把s0沒複製完的存活對象直接放入old區。求各位大神指點
這麼老的問題,同意廖大的回答,直接上代碼佐證:
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();
oop obj = NULL;
// Try allocating obj in to-space (unless too old)
if (old-&>age() &< tenuring_threshold()) {
obj = (oop) to()-&>allocate(s);
}
// Otherwise try allocating obj tenured
if (obj == NULL) {
obj = _next_gen-&>promote(old, s);
if (obj == NULL) {
handle_promotion_failure(old);
return old;
}
} else {
// 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
obj-&>incr_age();
age_table()-&>add(obj, s);
}
// Done, insert forward pointer to obj in this header
old-&>forward_to(obj);
return obj;
}
直接放入老年代,如果此時打開了自適應開關,GC結束後會調整新生代的大小
這種情況會觸發過早提升到年老帶,而且jvm對new有優化的,如果eden滿了也不一定要觸發gc,直接再年老帶new對象了。。。。粘一小段代碼,看到了這個注釋
// If certain conditions hold, try allocating from the old gen.
HeapWord* result = young_gen()-&>allocate(size);
while (result == NULL) {
{
MutexLocker ml(Heap_lock);
gc_count = Universe::heap()-&>total_collections();
result = young_gen()-&>allocate(size);
if (result != NULL) {
return resu<
}
// If certain conditions hold, try allocating from the old gen.
result = mem_allocate_old_gen(size);
if (result != NULL) {
return resu<
}
1。現在的新生代收集都必須停止虛擬機(牛逼的azul不知道),因為要移動內存里對象的位置,所有隻能停止。這樣的話你說的2是沒必要做的,因為虛擬機停止了,內存狀態肯定就沒變了。
等其他人貼詳細資料給你看吧。
1. 非GC時,新生代的分布是這樣,eden區和s0區有object,s1區為空;
2 eden區滿時,觸發MinorGC,將eden區和s0區尚存活的object,複製到s1區,順利完成後,s0區和s1區命名互換一下,返回情況1;
2.1 MinorGC 中存活的object 如果超過tenuring threshold,會promote到老生代;
3 每次MinorGC後,都會計算一個合理的tenuring threshold和各年代區的size,以及適時地調整size
(具體code在此:http://hg.openjdk.java.net/jdk9/hs-gc/hotspot/file/41e47efcd333/src/share/vm/gc/shared/ageTable.cpp 看compute_tenuring_threshold;
http://hg.openjdk.java.net/jdk9/hs-gc/hotspot/file/41e47efcd333/src/share/vm/gc/parallel/psAdaptiveSizePolicy.cpp 看 compute_eden_space_size compute_survivor_space_size_and_threshold 和 compute_old_gen_free_space)
4. 在2時,如果s1區提前滿了,就是undesired的premature promotion,雖然從jvm角度是想通過方法3來避免的,但也無法保證;
5. 老生代如果也滿了(可能是由2.1或4導致),那就沒什麼好說了,直接Full GC了;
ps:安利我的一點點小貢獻,在上述file里fix了幾個小bug,重構一下十幾年沒動的一些方法使之更輕鬆使在多個垃圾收集器演算法里。當時心情也是小激動的6666
我猜是1,等大神給答案……
問題:一直疑問這個問題,當eden區滿了,觸發垃圾回收,此時eden區存活的對象開始複製到s0區,複製過程中s0區滿了,可是eden區存活對象還沒複製完,此時jvm會怎麼辦?
答案:當Eden滿時,會觸發MinorGC演算法來回收memory,旨在清理掉再無引用的數據(在內存里是Tree),意圖存儲到S0. 若此時S0也滿了,會再次MinorGC意圖回收S0無引用的數據,把有引用的數據移動到S1。如果S1夠用,此時會清空S0;如果S1滿了,會回滾剛存入S1的數據,直接把本次GC的數據存入Old區,S0保持剛剛MinorGC時的狀態。
延伸:如果Old也滿了,會觸發MajorGC,如果還是不夠,則存入Permanent Generate,不幸這裡也滿了,會在允許的範圍內按照內置的規則自動增長,可能不會發生GC,也可能會。當增長的量不夠存時,會觸發Full GC。若FullGC後還是不夠存,自動增長的量也超過了允許的範圍,則發生內存溢出。還有一種情況,就是分配的線程棧處於很深的遞歸或死循環時,會發生棧內存溢出。
所以從這裡可以看出GC的頻率是 Eden &> S0 &> S1 &> Old &> Permanent,咱們在優化時盡量讓GC發生在Eden,避免大對象直接跨過S0, S1,而直接到Old了。另外,每次GC JVM都會停,GC後都會動態調整各區的大小,如果S1老是不來,常常會分配很小的內存,甚至是個位數。
閱讀過JVM英文原版原理和GC會直接進入old區,這種情況有個專門的術語,叫「Premature Promotion」,出現Premature Promotion會顯著增加full gc的次數,而full gc代價高昂,所以,這種情況會明顯降低程序的吞吐率和穩定性,要極力避免。
如果出現了這種情況,可以考慮給新生代分配更多的內存。
Eden區滿了,就會觸發一次minor GC,GC的時候發現survivor也他媽不夠用了,此時只好通過分配擔保機制提前轉移到老年代了…對於大對象,虛擬機也提供了優化機制,為了減少Eden到Survivor的複製成本,直接把對象放到了老年代,當然,這個大對象的界定可以通過參數來配置的
據我的印象,深入理解java虛擬機那本書里說的是這個時候就要靠老年代來做擔保,把對象放到老年代裡去。老年代也不足時就full gc一次。
推薦閱讀:
※Go1.6中的gc pause已經完全超越JVM了嗎?
※問下, C++ 的垃圾回收機制 如何實現的話 要怎麼掃描 bss data 之類的欄位?
※XMLHttpRequest對象的生命周期是如何管理的?
※為什麼 C++ 11 標準不加入 GC 功能呢?
※c++11標準 GC(垃圾回收)是否會使老代碼產生未定義行為?
TAG:Java | Java虛擬機JVM | GC垃圾回收計算機科學 |