Hotspot 内存管理之CodeCache 源码解析

本文详细探讨了Hotspot虚拟机的CodeCache内存管理,包括CodeCache的初始化、CodeHeap内存分配与释放、ReservedSpace和VirtualSpace的角色。CodeCache用于缓存汇编代码,而CodeHeap是其实现,通过HeapBlock和FreeBlock进行内存管理。在内存不足时,CodeHeap会通过expand_by方法扩展。
摘要由CSDN通过智能技术生成

  目录

一、CodeCache

1、定义

2、initialize

3、allocate / commit / free

4、blobs_do / nmethods_do

二、CodeHeap

1、定义

2、HeapBlock /FreeBlock 

3、reserve /expand_by

4、allocate

5、deallocate

6、 first_block / next_block

三、ReservedSpace

四、VirtualSpace


《Hotspot 方法调用之StubRoutines 源码解析》中讲解BufferBlob的初始化时,BufferBlob是通过CodeCache::allocate方法分配内存的,如下图所示:

本篇博客就顺着这个方法的实现来研究负责管理生成的汇编代码缓存的CodeCache的相关实现类。 

一、CodeCache

1、定义

      CodeCache就是用于缓存不同类型的生成的汇编代码,如热点方法编译后的代码,各种运行时的调用入口Stub等,所有的汇编代码在CodeCache中都是以CodeBlob及其子类的形式存在的。通常CodeBlob会对应一个CodeBuffer,负责生成汇编代码的生成器会通过CodeBuffer将汇编代码写入到CodeBlob中,参考《Hotspot 方法调用之StubGenerator 源码解析》中MacroAssembler类的说明。CodeCache的定义位于hotspot src/share/vm/code/codeCache.hpp中,包含的属性和方法都是静态的,属性不多,如下图:

其中_heap属性是实际负责内存管理的, _number_of开头的几个属性是统计计数的,_scavenge_root_nmethods是为了GC时遍历nmethod使用。CodeCache定义的关键方法主要有以下几种:

  • 内存分配和释放的,如allocate,commit,free等
  • 查找或者遍历已经存在的Blob和nmethod,如find_blob,find_nmethod,blobs_do,nmethods_do,first,next等
  • GC支持的,如gc_epilogue,gc_prologue,do_unloading,scavenge_root_nmethods_do等
  • 获取CodeCache对应内存块属性的,如low_bound,high_bound,capacity等
  • 逆优化相关的,如mark_for_deoptimization,mark_all_nmethods_for_deoptimization,make_marked_nmethods_not_entrant等

我们重点关注其初始化和内存分配相关方法的实现。

2、initialize

      initialize方法用来初始化CodeCache的,在JVM进程启动时执行,初始化结束后整个运行期通过_heap属性管理的内存都不会被释放,从而保证已生成的被频繁访问的汇编代码一直常驻内存。该方法的调用链如下:

codeCache_init方法的实现如下:

initialize方法实现的源码说明如下:

void CodeCache::initialize() {
//校验参数配置是否正确,CodeCacheSegmentSize表示CodeCache对应内存的最小值,CodeEntryAlignment表示分配给一段汇编代码的最小内存空间
  assert(CodeCacheSegmentSize >= (uintx)CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points");
#ifdef COMPILER2
//OptoLoopAlignment表示给内部循环代码分配的最低内存大小
  assert(CodeCacheSegmentSize >= (uintx)OptoLoopAlignment,  "CodeCacheSegmentSize must be large enough to align inner loops");
#endif
  assert(CodeCacheSegmentSize >= sizeof(jdouble),    "CodeCacheSegmentSize must be large enough to align constants");
  
  //按照系统的内存页大小对CodeCache的参数取整
  //CodeCacheExpansionSize表示CodeCache扩展一次内存空间对应的内存大小,x86下默认值是2304k
  CodeCacheExpansionSize = round_to(CodeCacheExpansionSize, os::vm_page_size());
  //InitialCodeCacheSize表示CodeCache的初始大小,x86 启用C2编译下,默认值是2304k
  InitialCodeCacheSize = round_to(InitialCodeCacheSize, os::vm_page_size());
  //ReservedCodeCacheSize表示CodeCache的最大内存大小,x86 启用C2编译下,默认值是48M
  ReservedCodeCacheSize = round_to(ReservedCodeCacheSize, os::vm_page_size());
  //完成heap属性的初始化
  if (!_heap->reserve(ReservedCodeCacheSize, InitialCodeCacheSize, CodeCacheSegmentSize)) {
    vm_exit_during_initialization("Could not reserve enough space for code cache");
  }
  //将CodeHeap放入一个MemoryPool中管理起来
  MemoryService::add_code_heap_memory_pool(_heap);

  //初始化用于刷新CPU指令缓存的Icache,即生成一段用于刷新指令缓存的汇编代码,此时因为heap属性已初始化完成,所以可以从CodeCache中分配Blob了
  icache_init();

  //通知操作系统我们的CodeCache的内存区域,主要是win64使用
  os::register_code_area(_heap->low_boundary(), _heap->high_boundary());
}

3、allocate / commit / free

     allocate方法用于分配CodeBlob的,当CodeBlob分配成功并且初始化完成时调用commit方法来增加CodeCache的相关统计计数;free方法用于释放一个已分配的CodeBlob并减少CodeCache的统计计数。

allocate方法的调用链如下:

其中BufferBlob,nmethod,RuntimeStub和SingletonBlob就是CodeBlob的四个直接子类。

commit方法的调用链如下:

其中AdapterBlob是BufferBlob的子类,之所以只有这两种Blob调用commit方法是因为CodeCache只单独统计了这两类Blob的数量。

free方法的调用链如下:

这三个方法的源码说明如下:

CodeBlob* CodeCache::allocate(int size, bool is_critical) {
  guarantee(size >= 0, "allocation request must be reasonable");
  //校验已经获取锁了,由此方法的调用方负责获取锁
  assert_locked_or_safepoint(CodeCache_lock);
  CodeBlob* cb = NULL;
  //增加计数器
  _number_of_blobs++;
  //不断循环
  while (true) {
    //分配CodeBlob
    cb = (CodeBlob*)_heap->allocate(size, is_critical);
    //分配成功
    if (cb != NULL) break;
    //分配失败,尝试扩展CodeCache,如果失败返回NULL
    if (!_heap->expand_by(CodeCacheExpansionSize)) {
      // Expansion failed
      return NULL;
    }
    //扩展CodeCache成功后继续尝试分配一个Blob
    //打印CodeCache扩展日志
    if (PrintCodeCacheExtension) {
      ResourceMark rm;
      tty->print_cr("code cache extended to [" INTPTR_FORMAT ", " INTPTR_FORMAT "] (" SSIZE_FORMAT " bytes)",
                    (intptr_t)_heap->low_boundary(), (intptr_t)_heap->high(),
                    (address)_heap->high() - (address)_heap->low_boundary());
    }
  }
  //更新已使用的CodeCache内存大小,maxCodeCacheUsed是一个静态变量
  maxCodeCacheUsed = MAX2(maxCodeCacheUsed, ((address)_heap->high_boundary() -
                          (address)_heap->low_boundary()) - unallocated_capacity());
  //校验CodeHeap
  verify_if_often();
  print_trace("allocation", cb, size);
  return cb;
}

void CodeCache::verify_if_often() {
  if (VerifyCodeCacheOften) {
    _heap->verify();
  }
}


void CodeCache::free(CodeBlob* cb) {
  assert_locked_or_safepoint(CodeCache_lock);
  verify_if_often();

  print_trace("free", cb);
  //根据CodeBlob的类型减少对应的计数器
  if (cb->is_nmethod()) {
    _number_of_nmethods--;
    if (((nmethod *)cb)->has_dependencies()) {
      _number_of_nmethods_with_dependencies--;
    }
  }
  if (cb->is_adapter_blob()) {
    _number_of_adapters--;
  }
  _number_of_blobs--;
  //释放CodeBlob
  _heap->deallocate(cb);

  verify_if_often();
  assert(_number_of_blobs >= 0, "sanity check");
}


void CodeCache::commit(CodeBlob* cb) {
  //校验已获取锁CodeCache_lock
  assert_locked_or_safepoint(CodeCache_lock);
  //根据CodeBlob的类型增加计数器
  if (cb->is_nmethod()) {
    _number_of_nmethods++;
    if (((nmethod *)cb)->has_dependencies()) {
      _number_of_nmethods_with_dependencies++;
    }
  }
  if (cb->is_adapter_blob()) {
    _number_of_adapters++;
  }

  //刷新CPU的汇编指令缓存
  ICache::invalidate_range(cb->content_begin(), cb->content_size());
}

4、blobs_do / nmethods_do

     blobs_do用于遍历所有的CodeBlob执行指定的函数,nmethods_do用于遍历所有的nmethod执行指定的函数,这两个方法实现的源码说明如下:

void CodeCache::blobs_do(void f(CodeBlob* nm)) {
  assert_locked_or_safepoint(CodeCache_lock);
  //FOR_ALL_BLOBS是一个遍历所有Blob的宏
  FOR_ALL_BLOBS(p) {
    f(p);
  }
}

void CodeCache::nmethods_do(void f(nmethod* nm)) {
  assert_locked_or_safepoint(CodeCache_lock);
  FOR_ALL_BLOBS(nm) {
    //判断Blob是否是nmethod
    if (nm->is_nmethod()) f((nmethod*)nm);
  }
}

//最终通过heap属性的方法完成遍历
#define FOR_ALL_BLOBS(var)       for (CodeBlob *var =       first() ; var != NULL; var =       next(var) )

CodeBlob* CodeCache::first() {
  assert_locked_or_safepoint(CodeCache_lock);
  return (CodeBlob*)_heap->first();
}


CodeBlob* CodeCache::next(CodeBlob* cb) {
  assert_locked_or_safepoint(CodeCache_lock);
  return (CodeBlob*)_heap->next(cb);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值