zoukankan      html  css  js  c++  java
  • Runtime AppCDS

    1. 概念与流程

    CDS介绍

    传统CDS[0]分为Dump和Use两个大阶段:

    -Xshare:off -XX:DumpLoadedClassList=test.log
    
    -Xshare:dump -XX:SharedClassListFile=test.log -XX:SharedArchiveFile=test.jsa
    
    -Xshare:on -XX:SharedArchiveFile=test.jsa
    

    实际上为了使用CDS,一般还是需要三步。第一步产生一个包含类名字的列表test.log,第二步根据类名字列表产生CDS archive,第三步使用CDS Archive。可以搜索DumpSharedSpaces和UseSharedSpaces两个flag来找到后面两步的代码和逻辑。

    CDS Dump完整流程位于MetaspaceShared::preload_and_dump,它先做一些初始化工作,比如读取classlist的类,并预加载,然后VMThread::execute一个VM_PopulateDumpSharedSpace做实际dump archive工作:

    void MetaspaceShared::preload_and_dump(TRAPS) {
      { TraceTime timer("Dump Shared Spaces", TRACETIME_LOG(Info, startuptime));
        ResourceMark rm;
        char class_list_path_str[JVM_MAXPATHLEN];
        // Preload classes to be shared.
        // Should use some os:: method rather than fopen() here. aB.
        const char* class_list_path;
        if (SharedClassListFile == NULL) {
          // Construct the path to the class list (in jre/lib)
          // Walk up two directories from the location of the VM and
          // optionally tack on "lib" (depending on platform)
          os::jvm_path(class_list_path_str, sizeof(class_list_path_str));
          for (int i = 0; i < 3; i++) {
            char *end = strrchr(class_list_path_str, *os::file_separator());
            if (end != NULL) *end = '\0';
          }
          int class_list_path_len = (int)strlen(class_list_path_str);
          if (class_list_path_len >= 3) {
            if (strcmp(class_list_path_str + class_list_path_len - 3, "lib") != 0) {
              if (class_list_path_len < JVM_MAXPATHLEN - 4) {
                jio_snprintf(class_list_path_str + class_list_path_len,
                             sizeof(class_list_path_str) - class_list_path_len,
                             "%slib", os::file_separator());
                class_list_path_len += 4;
              }
            }
          }
          if (class_list_path_len < JVM_MAXPATHLEN - 10) {
            jio_snprintf(class_list_path_str + class_list_path_len,
                         sizeof(class_list_path_str) - class_list_path_len,
                         "%sclasslist", os::file_separator());
          }
          class_list_path = class_list_path_str;
        } else {
          class_list_path = SharedClassListFile;
        }
    
        tty->print_cr("Loading classes to share ...");
        _has_error_classes = false;
        int class_count = preload_classes(class_list_path, THREAD);
        if (ExtraSharedClassListFile) {
          class_count += preload_classes(ExtraSharedClassListFile, THREAD);
        }
        tty->print_cr("Loading classes to share: done.");
    
        log_info(cds)("Shared spaces: preloaded %d classes", class_count);
    
        // Rewrite and link classes
        tty->print_cr("Rewriting and linking classes ...");
    
        // Link any classes which got missed. This would happen if we have loaded classes that
        // were not explicitly specified in the classlist. E.g., if an interface implemented by class K
        // fails verification, all other interfaces that were not specified in the classlist but
        // are implemented by K are not verified.
        link_and_cleanup_shared_classes(CATCH);
        tty->print_cr("Rewriting and linking classes: done");
    
        SystemDictionary::clear_invoke_method_table();
        HeapShared::init_archivable_static_fields(THREAD);
    
        VM_PopulateDumpSharedSpace op;
        VMThread::execute(&op);
      }
    }
    

    以上是CDS Dump的大流程。JDK16上,JDK-8253909可以输出详细的cds archive文件内容,有助于debug。

    (info = high level layout -- regions, etc)
    java -Xshare:dump -Xlog:cds+map=info:file=cds.map:none:filesize=0
    
    (debug = information about all metaspace objects)
    java -Xshare:dump -Xlog:cds+map=debug:file=cds.map:none:filesize=0
    
    (trace = values of every byte in the archive)
    java -Xshare:dump -Xlog:cds+map=trace:file=cds.map:none:filesize=0
    

    2. CDS Shared Archive

    cds shared archive有五个区域

    • mc - misc code (method entry trampoline code)
    • rw - read-write metadata
    • ro - read-only space
    • md - misc data (c++ vtable)
    • od - optional data (original class file)

    这五个区域按顺序分配,base addr可以通过-XX:SharedBaseAddress指定。分配过程则是在jvm启动的时候,初始化Metaspace::global_initialize中完成的。一个一个来。

    2.1 mc - method entry

    模板解释器在jvm创建的时候会为每种方法类型(zerolocal,synchronized,native...)生成entry(TemplateInterpreterGenerator::generate_all)。在开启appcds的情况下,还会多一个(update_cds_entry_table)步骤,即为cds_entry_table设置入口:
    image

    image

    也就是说,模板解释器generate_all完之后,存在两张table,一张正常的_entry_table,一张_cds_entry_table,其中cds_entry_table的每个entry指向一段trampoline代码,这段代码跳到正常的entry_table的entry。

    那么到底该用哪张表呢?这一步由Method::link_method决定:

    void Method::link_method(TRAPS){
      if(is_shared(){
        use_cds_entry_table();
      }else{
      	use_entry_table();
      }
      ...
    }
    

    这一步还有一个疑问,它只为cds_entry_table设置了对应的cds entry,但是没有为_deopt_table,_safept_table, exception_entries这些设置?

    2.2 rw和ro regions

    当迭代下面roots的时候,jvm会区分哪些是rw数据,哪些是ros数据,然后分别写入各自的区域

    static void iterate_roots(MetaspaceClosure* it) {
        GrowableArray<Symbol*>* symbols = _ssc->get_sorted_symbols();
        for (int i=0; i<symbols->length(); i++) {
          it->push(symbols->adr_at(i));
        }
        if (_global_klass_objects != NULL) {
          for (int i = 0; i < _global_klass_objects->length(); i++) {
            it->push(_global_klass_objects->adr_at(i));
          }
        }
        FileMapInfo::metaspace_pointers_do(it);
        SystemDictionary::classes_do(it);
        if (NotFoundClassOpt && SystemDictionary::not_found_class_table()) {
          SystemDictionary::not_found_class_table()->metaspace_pointers_do(it);
        }
        Universe::metaspace_pointers_do(it);
        SymbolTable::metaspace_pointers_do(it);
        vmSymbols::metaspace_pointers_do(it);
      }
    

    上面这些root中,在metaspace存放的数据大部分都是rw,其他都是ro。

    2.3 md - cpp vtable

    [0] https://openjdk.java.net/jeps/310
    [1] http://cr.openjdk.java.net/~jiangli/Leyden/Java%20Class%20Pre-resolution%20and%20Pre-initialization%20(OpenJDK).pdf
    [2] http://cr.openjdk.java.net/~jiangli/Leyden/Selectively%20Pre-initializing%20and%20Preserving%20Java%20Classes%20(OpenJDK).pdf

    3. code insight

    下面是一个标准路径,也就是ClassLoader.loadClass的过程中检查CDS archive看是否有klass
    // [0] SystemDictionaryShared::find_or_load_shared_class()
    // [1] JVM_FindLoadedClass
    // [2] java.lang.ClassLoader.findLoadedClass0()
    // [3] java.lang.ClassLoader.findLoadedClass()
    // [4] jdk.internal.loader.BuiltinClassLoader.loadClassOrNull()
    // [5] jdk.internal.loader.BuiltinClassLoader.loadClass()
    // [6] jdk.internal.loader.ClassLoaders\(AppClassLoader.loadClass(), or // jdk.internal.loader.ClassLoaders\)PlatformClassLoader.loadClass()

    除此之外,还有一些,如JVM_DefineClass1也有机会走到CDS 的逻辑。

  • 相关阅读:
    2015年要做的150件事
    再见2014,你好2015
    页面滚动事件的使用
    简单的进度条演示
    mybatis foreach批量处理
    mysql执行顺序
    spring声明式事务 同一类内方法调用事务失效
    Semaphore
    springmvc使用JSR-303对复杂对象进行校验
    springmvc全局异常后返回JSON异常数据
  • 原文地址:https://www.cnblogs.com/kelthuzadx/p/15726611.html
Copyright © 2011-2022 走看看