前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >自从学了深入解析java虚拟机:FullGC和字符串去重后,我无敌了

自从学了深入解析java虚拟机:FullGC和字符串去重后,我无敌了

作者头像
愿天堂没有BUG
发布2022-10-31 11:28:35
6530
发布2022-10-31 11:28:35
举报

Full GC

在设计G1时会极力避免Full GC(以下简称FGC),但是总有一些特殊情况,如果当前并发回收的速度跟不上对象分配的速度,那么需要G1启动后备方案进行FGC。早期G1的FGC使用单线程的标记整理算法,后来为了充分发挥多核处理器的优势,JEP 307提案为G1的FGC设计了多线程标记整理算法,此时多线程的FGC的线程数量可以由-XX:ParallelGCThreads控制。

G1的多线程FGC与Parallel GC的FGC类似,是一个全局STW的过程,G1使用线程组完成垃圾回收工作,整个阶段都不允许Mutator线程运行。FGC的实现位于G1FullCollector::collect(),如代码清单11-7所示:

代码清单11-7 G1 FGC

代码语言:javascript
复制
void G1FullCollector::collect() {
phase1_mark_live_objects();
phase2_prepare_compaction();
phase3_adjust_pointers();
phase4_do_compaction();
}

正如之前所说,FGC是一个标准的标记整理算法,每个步骤提交任务给线程池,使用多线程完成,尽量减少STW时间。触发FGC的场景有很多,举例如下:

Mixed GC中如果老年代回收的速度小于对象分配或晋升的速度,会触发FGC;

YGC最后会移动存活对象到其他分区,如果此时发现没有能容纳存活对象的Region,会触发FGC;

如果没有足够的Region容纳下Humongous对象,会触发FGC;

应用程序调用System.gc()也会触发FGC。

由于FGC的全局STW性,如果频繁发生FGC是比较糟糕的信号,它暗示应用程序的特性与当前的G1参数配置不能良好契合,需要开发者找到问题并进一步调优处理。

字符串去重

如果读者对虚拟机进行过Heap Dump(-XX:+ HeapDumpOnOutOfMemoryError或者jmap触发)操作,会观察到Java堆中占比最大的通常是一些byte[]对象,这些byte[]对象又通常是String的成员,即字符串对象在Java堆中占据极大比重,如果能发现重复的字符串并消除它们,会节省很大一部分内存。可以手动调用String.intern()消除重复的字符串,但这需要开发者了解哪些字符串可能发生重复,也可以使用G1的新特性自动完成字符串去重。

G1的YGC和FGC都可以触发字符串去重,只需要开启-XX:+UseStringDeduplication。在YGC的copy_to_survivor()过程中如果发现开启了自动去重选项,G1会调用 G1StringDedup::enqueue_from_evacuation()自动发现可以去重的字符串,如代码清单11-8所示:

代码清单11-8 选择重复字符串

代码语言:javascript
复制
bool G1StringDedup::is_candidate_from_evacuation(...) {
// 如果对象在Eden Region,并且类型是java.lang.String
if (from_young && java_lang_String::is_instance_inlined(obj)) {
// 如果对象将要复制到Survivor Region,并且年龄小于阈值
if (to_young && obj->age() == StringDeduplicationAgeThreshold) {
return true; // 作为候选项加入G1StringDedupQueue
}
// 如果对象将要晋升到Old Region,并且年龄小于阈值if (!to_young && obj->age() < StringDeduplicationAgeThreshold) {
return true; // 作为候选项加入G1StringDedupQueue
}
}
return false;
}
void G1StringDedup::enqueue_from_evacuation(...) {
if (is_candidate_from_evacuation(...)) {
G1StringDedupQueue::push(worker_id, java_string);
}
}

G1将所有存活对象从Eden复制到Survivor Region,所有从Eden晋升到Old Region并且年龄小于 -XX:StringDeduplicationAgeThreshold的对象都会被放入G1StringDedupQueue等待字符串去重线程处理。字符串去重线程即StringDedupThread,它在发现队列中存在去重候选项后会弹出对象,然后调用StringDedupTable::deduplicate,如代码清单11-9所示:

代码清单11-9 StringDedupTable::deduplicate

代码语言:javascript
复制
void StringDedupTable::deduplicate(...) {
// 如果java.lang.String的value字段为空,那么不处理
typeArrayOop value = java_lang_String::value(java_string);
if (value == NULL) {
stat->inc_skipped();
return;}
...
// 根据新对象的hash查找已有对象
typeArrayOop existing_value = lookup_or_add(value, latin1, hash);
// 如果新对象和已有对象是同一个,那么不处理
if (oopDesc::equals_raw(existing_value, value)) {
stat->inc_known();
return;
}
... // 如果是不同对象,但是包含的字符串相同,则处理它
if (existing_value != NULL) {
java_lang_String::set_value(java_string, existing_value);
stat->deduped(value, size_in_bytes);
}
}

本文给大家讲解的内容是深入解析java虚拟机:Full GC和字符串去重

  1. 觉得文章不错的朋友可以转发此文关注小编;
  2. 感谢大家的支持!

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 愿天堂没有BUG 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Full GC
  • 字符串去重
  • 本文给大家讲解的内容是深入解析java虚拟机:Full GC和字符串去重
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档