• Hotpot 年轻代GC 源代码分析


    图解分析:https://www.cnblogs.com/lqlqlq/p/13912325.html

    Hotspot为每一个代 都设置了 各自的 level,也就是等级,年轻代 level 一般为 0, 老年代 level 一般为 1。

    在默认的年轻代(/vm/memory/defNewGeneration.cpp)下的实现:

    defNewGeneration::collect 有如下重要调用点:

    1. gch->gen_process_strong_roots(_level,
                                    true,  // Process younger gens, if any,
                                           // as strong roots.
                                    true,  // activate StrongRootsScope
                                    false, // not collecting perm generation.
                                    SharedHeap::SO_AllClasses,
                                    &fsc_with_no_gc_barrier,
                                    true,   // walk *all* scavengable nmethods
                                    &fsc_with_gc_barrier);
      // "evacuate followers".
    2. evacuate_followers.do_void();
    3. rp->process_discovered_references(&is_alive, &keep_alive, &evacuate_followers,
                                        NULL);

    重要的闭包:FastScanClosure :: do_oop_work :将 接收的参数是 T * (其实也就是 oop *) , 是指向 oop 的指针

    oop 本身就是 oopDesc * 的缩写,也就是 java 在 C++ 层面的对象的指针,所以修改 T* 指向的内存单元,就可以修改这个内存单元指向的oop,相当于修改对象的引用类型变量

    入参的 do_barriar 如果为 true,则在处理 T* 时,需要将该引用指向的地址的卡表,设置为 younggen_card (未知作用)

     概述:

    1.gen_process_strong_roots 

    处理根节点引用的oop,包括Java方法堆栈,JNI方法堆栈,符号表等引用的对象

    2.evacuate_followers

    负责将 从年轻代promote到老年代的对象,使用 入参为 true 的 FastScanClosure 作为参数,调用这些对象的 oop_iterate, 也就是遍历这些对象的引用类型 

    也就是遍历这些对象的引用类型变量,如果年龄到阈值,则promote到老年代,否则复制到 to 区

    3.处理Java提供的各种非强引用

    1.gen_process_strong_roots 

    该方法的实现在 GenCollectedHeap 中

    // General strong roots.
    
      if (!do_code_roots) {
        SharedHeap::process_strong_roots(activate_scope, collecting_perm_gen, so,
                                         not_older_gens, NULL, older_gens);
      } else {
        bool do_code_marking = (activate_scope || nmethod::oops_do_marking_is_active());
        CodeBlobToOopClosure code_roots(not_older_gens, /*do_marking=*/ do_code_marking);
        SharedHeap::process_strong_roots(activate_scope, collecting_perm_gen, so,
                                         not_older_gens, &code_roots, older_gens);
      }
    
      if (younger_gens_as_roots) {
        if (!_gen_process_strong_tasks->is_task_claimed(GCH_PS_younger_gens)) {
          for (int i = 0; i < level; i++) {
            not_older_gens->set_generation(_gens[i]);
            _gens[i]->oop_iterate(not_older_gens);
          }
          not_older_gens->reset_generation();
        }
      }
      // When collection is parallel, all threads get to cooperate to do
      // older-gen scanning.
      for (int i = level+1; i < _n_gens; i++) {
        older_gens->set_generation(_gens[i]);
        rem_set()->younger_refs_iterate(_gens[i], older_gens);
        older_gens->reset_generation();
      }
        younger_refs_in_space_iterate
      _gen_process_strong_tasks->all_tasks_completed();

    最重要的三个点是

    1.process_strong_roots  对根节点使用某个特定的 closure,上文中是 not_older_gens,即不是针对老年代的 Clsoure

    2.第一个循环的 oop_iterate  倘若是老年代调用 gen_process_strong_roots ,则level是1,也就是会调用循环中的本 oop_iterate , 年轻代的level 是 0,所以不会调用。本oop_iterate是使用 not_older_gens 的 closure 去处理oop的引用类型

    3.第二个循环的 younger_refs_iterate  是用来遍历脏卡表的,上面用的是 older_gens 去遍历脏卡表对应区域引用的对象

    1.process_strong_roots

    void SharedHeap::process_strong_roots(bool activate_scope,
                                          bool collecting_perm_gen,
                                          ScanningOption so,
                                          OopClosure* roots,
                                          CodeBlobClosure* code_roots,
                                          OopsInGenClosure* perm_blk) {
      StrongRootsScope srs(this, activate_scope);
      // General strong roots.
      assert(_strong_roots_parity != 0, "must have called prologue code");
      if (!_process_strong_tasks->is_task_claimed(SH_PS_Universe_oops_do)) {
        Universe::oops_do(roots);   //1
        ReferenceProcessor::oops_do(roots);
        // Consider perm-gen discovered lists to be strong.
        perm_gen()->ref_processor()->weak_oops_do(roots);
      }
      // Global (strong) JNI handles
      if (!_process_strong_tasks->is_task_claimed(SH_PS_JNIHandles_oops_do))
        JNIHandles::oops_do(roots);  //2
      // All threads execute this; the individual threads are task groups.
      if (ParallelGCThreads > 0) {
        Threads::possibly_parallel_oops_do(roots, code_roots);
      } else {
        Threads::oops_do(roots, code_roots);  //3
      }
      if (!_process_strong_tasks-> is_task_claimed(SH_PS_ObjectSynchronizer_oops_do))
        ObjectSynchronizer::oops_do(roots);  //4
      if (!_process_strong_tasks->is_task_claimed(SH_PS_FlatProfiler_oops_do))
        FlatProfiler::oops_do(roots);  //5
      if (!_process_strong_tasks->is_task_claimed(SH_PS_Management_oops_do))
        Management::oops_do(roots);  //6
      if (!_process_strong_tasks->is_task_claimed(SH_PS_jvmti_oops_do))
        JvmtiExport::oops_do(roots);  //7
    
      if (!_process_strong_tasks->is_task_claimed(SH_PS_SystemDictionary_oops_do)) {
        if (so & SO_AllClasses) {
          SystemDictionary::oops_do(roots);  //8
        } else if (so & SO_SystemClasses) {
          SystemDictionary::always_strong_oops_do(roots);
        }
      }
    
      if (!_process_strong_tasks->is_task_claimed(SH_PS_StringTable_oops_do)) {
        if (so & SO_Strings || (!collecting_perm_gen && !JavaObjectsInPerm)) {
          StringTable::oops_do(roots);  //9
        }
        if (JavaObjectsInPerm) {
          // Verify the string table contents are in the perm gen
          NOT_PRODUCT(StringTable::oops_do(&assert_is_perm_closure));
        }
      }
    
      if (!_process_strong_tasks->is_task_claimed(SH_PS_CodeCache_oops_do)) {
        if (so & SO_CodeCache) {
          // (Currently, CMSCollector uses this to do intermediate-strength collections.)
          assert(collecting_perm_gen, "scanning all of code cache");
          assert(code_roots != NULL, "must supply closure for code cache");
          if (code_roots != NULL) {
            CodeCache::blobs_do(code_roots);
          }
        } else if (so & (SO_SystemClasses|SO_AllClasses)) {
          if (!collecting_perm_gen) {
            // If we are collecting from class statics, but we are not going to
            // visit all of the CodeCache, collect from the non-perm roots if any.
            // This makes the code cache function temporarily as a source of strong
            // roots for oops, until the next major collection.
            //
            // If collecting_perm_gen is true, we require that this phase will call
            // CodeCache::do_unloading.  This will kill off nmethods with expired
            // weak references, such as stale invokedynamic targets.
            CodeCache::scavenge_root_nmethods_do(code_roots);
          }
        }
        // Verify that the code cache contents are not subject to
        // movement by a scavenging collection.
        DEBUG_ONLY(CodeBlobToOopClosure assert_code_is_non_scavengable(&assert_is_non_scavengable_closure, /*do_marking=*/ false));
        DEBUG_ONLY(CodeCache::asserted_non_scavengable_nmethods_do(&assert_code_is_non_scavengable));
      }
    
      if (!collecting_perm_gen) {
        // All threads perform this; coordination is handled internally.
    
        rem_set()->younger_refs_iterate(perm_gen(), perm_blk);
      }
      _process_strong_tasks->all_tasks_completed();
    }

    1.Universe : 某些全局量持有的对象引用,比如:

       

      mirror 是某个类静态遍历的集合,比如说有 Class A { static int a ; int b; static A c;}

      那么这个类的 mirror 就是 一个 12 字节的 oop,其实只是 12 字节的连续内存 区块。其中存放了 4 字节的 int 类型变量,8字节的指针类型变量

      上面这些 mirror 都是 java 提供的基础类型 的类 的 静态变量集合。每个集合 在 jvm 的 C++ 层面称为 oopDesc 。 oop 是 oopDesc * , 即oopDesc 的指针

       f 在当前语境下是 FastScanClosure,并且 处理 oop * 后不会把这个 oop * 的卡置为 youngercard,FastScanClosure的处理逻辑如下:把 oop * 的值引用的 oopDesc 复制到 to 或 老年代(年龄够了的情况下)

       注意 oop * 是 oopDesc 的指针的指针,也就是 oop * 是一个8字节的内存地址,指向的地方的8字节处是 oop,也是 一个 8 字节的内存地址,指向的地方是一个 oopDesc

       所以通过 f 修改 oop * 指向的内存区域,就是在修改某个 C++ 层面的类或结构(此处是 Universe)的指针域

       

    关于 f :do_oop 会调用 do_oop_work

    下面的逻辑是,如果对象没有被复制到别的地方,也就是 forwardee 指针是空,就尝试把对象复制到 to 区,或 promote 到 老年代

    针对年轻代的 _gc_barrier 是 false

    do_barrier 会把 oop * 在卡表中对应字节(一个字节代表一张卡)的值设置为youngercard

     

    每种 generation 都有自己 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();
      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;
    }

    上面的1~9就是所谓的根节点,其中比较重要的几个是 Universe,java线程栈中的oop ,jni持有的 oop

    3.younger_refs_iterate

    这个方法会遍历脏卡表项对应内存区的对象的引用,而且是针对老年代的。

    比如老年代有个对象 A 持有一个引用 b 指向 对象 B,实际上, A 和 B 都是 oopDesc, b 在 c ++ 层面就是一个 oop,也就是 oopDesc *

    当 A 的 b 引用 指向了其他对象,也就是 A.b = xxx , 那么 A 的 b 此时会指向 xxx 这个 oopDesc,此时 A 对应内存区的卡 就是一片脏卡,对应的卡表项就是脏的。

    所以要用 f 处理 A, 在处理之前会把 A 的内存对应卡表项置位干净卡。f 的入参是 A 的各个 oop 的地址

    比如 A 在 C ++ 层面的内存表示是:

  • 相关阅读:
    Android应用安全之外部动态加载DEX文件风险
    Android应用安全之Android APP通用型拒绝服务漏洞
    利用Cydia Substrate进行Android HOOK(二)
    Apk去签名校验详解
    nginx部署前端项目的一些配置【刚入门】
    java 日期格式化 将String日期重新格式化成String型【转】
    idea导入maven项目时需要注意
    element 'dependency' cannot have character [children], because the type's content type is element-only.
    腾讯云centos7执行 systemctl status postfix.service 启动postfix服务时出现 /usr/sbin/postconf: fatal: parameter inet_interfaces: no local interface found for ::1
    redis.clients.jedis.exceptions.JedisExhaustedPoolException: Could not get a resource since the pool is exhausted
  • 原文地址:https://www.cnblogs.com/lqlqlq/p/13907491.html
Copyright © 2020-2023  润新知