【jvm学习笔记七】G1-字符串去重

字符串去重是G1引入的新特性,在我们日常开发中,字符串基本上是使用最多的类型。而String对象是不可变的,通常会消耗大量的内存,这里面有一部分是冗余的。String对象有自己的专属char[],而这个char没有暴露给客户端。那么jvm就可以通过判断两个字符串是否一致,从而将一个字符串的char[]共享为另一个字符串的char[],从而达到节省内存的目的。

字符串去重步骤

  1. 找到能去重的对象
    字符串是否可以去重需要满足2个条件:1. 字符串位于新生代,若是复制到s区,则年龄>StringDeduplicationAgeThreshold(默认为3),才可以去重;若是字符串晋升到old区,且非大对象且年龄<StringDeduplicationAgeThreshold,则可以去重;2. fullgc只考虑第二个条件
bool G1StringDedup::is_candidate_from_mark(oop obj) {
  if (java_lang_String::is_instance(obj)) {
    bool from_young = G1CollectedHeap::heap()->heap_region_containing_raw(obj)->is_young();
    if (from_young && obj->age() < StringDeduplicationAgeThreshold) {
      //young to old且年龄<StringDeduplicationAgeThreshold
      return true;
    }
  }
  return false;
}

void G1StringDedup::enqueue_from_mark(oop java_string) {
  assert(is_enabled(), "String deduplication not enabled");
  if (is_candidate_from_mark(java_string)) {
    G1StringDedupQueue::push(0 /* worker_id */, java_string);
  }
}

bool G1StringDedup::is_candidate_from_evacuation(bool from_young, bool to_young, oop obj) {
  if (from_young && java_lang_String::is_instance(obj)) {
    if (to_young && obj->age() == StringDeduplicationAgeThreshold) {
      //young to young,即到s区,且年龄>StringDeduplicationAgeThreshold
      return true;
    }
    if (!to_young && obj->age() < StringDeduplicationAgeThreshold) {
      //young to old且年龄<StringDeduplicationAgeThreshold
      return true;
    }
  }

  // Not a candidate
  return false;
}
  1. 去重
    由单独的去重线程完成。开始去重时,会先查找字符数组是否存在,若存在则调整指针,共享字符串数组,释放多余的字符串数组。
    去重在G1CollectHeap初始化中启动,由hashtable存储字符串数组的值,在字符串回收时可以对hashtable进行遍历。
  2. 回收
    在ygc、并发标记、fullgc中都有可能进行字符串去重的回收。

参数

字符串去重的参数有UseStringDeduplication和StringDeduplicationAgeThreshold,前者代表是否开启去重功能,后者如上述。
虽然字符串去重能明显减少内存,但增加了gc处理时间,使用时需验证效果。

字符串去重和String.intern方法

String.intern方法:通过StringTable(HashTable实现)存储字符串对象,如果StringTable已存在该对象,则返回该对象在常量池中的引用。否则,在StringTable加入该对象,然后返回引用。
两者区别有2点:

  1. intern缓存的是字符串对象,而字符串去重是字符串里的字符数组。这里intern这么搞主要是gc并发标记用。试想如果intern使用字符数组的话,那么当Str1死亡还未回收时,另一个Str2执行intern则发现在Stringtable中没有,又会加入到Stringtable中,而此时只需激活Str1并返回引用即可。这里假设Str1和Str2是equals。
  2. intern需要显式调用,字符串去重是jvm自己处理。
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容