Skip to content

share C2_CompilerThread

ceragon edited this page Dec 8, 2021 · 3 revisions

C2 CompilerThread

声明

  1. 声明位于 compileBroker.cpp 中
void CompileBroker::init_compiler_sweeper_threads() {
    JavaThread *ct = make_thread(thread_handle, _c2_compile_queue, _compilers[1], CHECK);
}
  1. 创建位于make_thread 中, 我们只关心创建和启动过程,所以省略无关代码
JavaThread* CompileBroker::make_thread(jobject thread_handle, CompileQueue* queue, AbstractCompiler* comp, TRAPS) {
    JavaThread* thread = NULL;
    CompilerCounters* counters = new CompilerCounters();
    thread = new CompilerThread(queue, counters);
    Threads::add(thread);
    Thread::start(thread);
}

run 方法

  1. 线程的 run 方法如下, 同样省略不关心的代码
void JavaThread::run() {
    thread_main_inner();
}
  1. 主要是执行 thread_main_inner() 这个方法
void JavaThread::thread_main_inner() {
    // Execute thread entry point unless this thread has a pending exception
    // or has been stopped before starting.
    // Note: Due to JVM_StopThread we can have pending exceptions already!
    if (!this->has_pending_exception() &&
      !java_lang_Thread::is_stillborn(this->threadObj())) {
        this->entry_point()(this, this);
    }
}
  1. 其中官方注释写的很清楚,如果没有异常或者在启动中被stop,那么就可以正常执行线程的 entry point。对于C2 编译线程来说,它的callPoint是下面位置
CompilerThread::CompilerThread(CompileQueue* queue,
                               CompilerCounters* counters)
                               : JavaThread(&compiler_thread_entry) {
}
  1. 也就是构造线程对象的时候,entry point 就赋值给父类了,compiler_thread_entry 内容如下:
static void compiler_thread_entry(JavaThread* thread, TRAPS) {
    CompileBroker::compiler_thread_loop();
}

主逻辑

  1. 本thread的主要逻辑如下所示
void CompileBroker::compiler_thread_loop() {
    CompilerThread* thread = CompilerThread::current();
    CompileQueue* queue = thread->queue();
    while (!is_compilation_disabled_forever()) {
        CompileTask* task = queue->get(); 
        // UseCompiler 默认是 true, AlwaysCompileLoopMethods 默认是 false
        // 除非禁用 运行时编译,否则 should_compile_new_jobs 为 true
        if ((UseCompiler || AlwaysCompileLoopMethods) && CompileBroker::should_compile_new_jobs()) {
            invoke_compiler_on_method(task);
        }
        // 默认是 true
        if (UseDynamicNumberOfCompilerThreads) {
            possibly_add_compiler_threads();
        }
    }
}
  1. 上面代码的主要逻辑是执行 invoke_compiler_on_method,这个方法说实话特别复杂,但我主要关心输出是什么,以及存放到了什么位置。所以以下代码省略无关内容
void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
    AbstractCompiler* comp = task->compiler();
    comp->compile_method(&ci_env, target, osr_bci, directive);
}
  1. 继续追踪 compile_method
void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive) {
    while (!env->failing()) {
        Compile C(env, this, target, entry_bci, subsume_loads, do_escape_analysis, eliminate_boxing, directive);
    }
}
  1. 编译的主体逻辑在 Compile 的构造函数中, 我完全看不懂里面的内容,但我知道这个方法一定会产生一个输出结果,所以省略无关代码,只看输出内容
Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr_bci,
                  bool subsume_loads, bool do_escape_analysis, bool eliminate_boxing, DirectiveSet* directive){
    // 我认识这两个单词,代码生成
    Code_Gen();
}
  1. 貌似这个Code_Gen 方法才是主体,里面的逻辑我还是看不懂,所以你懂的
void Compile::Code_Gen() {
    // 我也认识这个单词,输出或者导出
    Output();
}
  1. 看下 Output 方法
void Compile::Output() {
    uint* blk_starts = NEW_RESOURCE_ARRAY(uint, _cfg->number_of_blocks() + 1);
    blk_starts[0] = 0;

    // Initialize code buffer and process short branches.
    CodeBuffer* cb = init_buffer(blk_starts);
    fill_buffer(cb, blk_starts);
}
  1. 我不关心往cb里中填充了什么内容,我只关心这些内容存在了内存的什么位置,可以看到主要是 NEW_RESOURCE_ARRAY 这个方法

C2 编译的内存占用分析

  1. C2 线程申请内存的代码
#define NEW_RESOURCE_ARRAY(type, size)\
  (type*) resource_allocate_bytes((size) * sizeof(type))
  1. 实际方法
extern char* resource_allocate_bytes(size_t size, AllocFailType alloc_failmode) {
    return Thread::current()->resource_area()->allocate_bytes(size, alloc_failmode);
}
  1. allocate_bytes 应该就是分配空间的方法,关键要看 resource_area() 这个方法导致是什么区域
Thread::Thread() {
    set_resource_area(new (mtThread)ResourceArea());
}
  1. ResourceArea 的构造方法如下:
ResourceArea(MEMFLAGS flags = mtThread) : Arena(flags) {
}
Arena::Arena(MEMFLAGS flag) : _flags(flag), _size_in_bytes(0) {
    _first = _chunk = new (AllocFailStrategy::EXIT_OOM, Chunk::init_size) Chunk(Chunk::init_size);
    _hwm = _chunk->bottom();      // Save the cached hwm, max
    _max = _chunk->top();
    MemTracker::record_new_arena(flag);
    set_size_in_bytes(Chunk::init_size);
}
  1. 接着再来看 allocate_bytes 这个方法
inline char* ResourceArea::allocate_bytes(size_t size, AllocFailType alloc_failmode) {
    return (char*)Amalloc(size, alloc_failmode);
}
void* Amalloc(size_t x, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {
    if (!check_for_overflow(x, "Arena::Amalloc", alloc_failmode))
      return NULL;
    
}

Clone this wiki locally