代码缓存(3)

代码缓存(3)

 

 


2、CodeBuffer

CodeBuffer类似于IO里面的BufferedReader等用来临时缓存生成的汇编代码,CodeBuffer用来缓存汇编代码的内存通常是BufferBlob中content部分的内存,也可以是其他的已分配的一片内存,可参考CodeBuffer的构造函数。  

CodeCache就是用于缓存不同类型的生成的汇编代码,如热点方法编译后的代码,各种运行时的调用入口Stub等,所有的汇编代码在CodeCache中都是以CodeBlob及其子类的形式存在的。

通常CodeBlob会对应一个CodeBuffer,负责生成汇编代码的生成器会通过CodeBuffer将汇编代码写入到CodeBlob中。

A CodeBuffer describes a memory space into which assembly code is generated. This memory space usually occupies the
interior(内部; 里面) of a single BufferBlob, but in some cases it may be an arbitrary span of memory, even outside the code cache.

A code buffer comes in two variants:

(1) A CodeBuffer referring to an already allocated piece of memory:
This is used to direct ‘static’ code generation (e.g. for interpreter or stubroutine generation, etc.). This code comes with NO relocation information.

(2) A CodeBuffer referring to a piece of memory allocated when the CodeBuffer is allocated. This is used for nmethod generation.

The memory can be divided up into several parts called sections.
Each section independently accumulates code (or data) an relocations.
Sections can grow (at the expense of 以…为代价 a reallocation of the BufferBlob and recopying of all active sections).

When the buffered code is finally written to an nmethod (or other CodeBlob),

the contents (code, data,and relocations) of the sections are padded to an alignment and concatenated.连接
Instructions and data in one section can contain relocatable references to addresses in a sibling section.

The structure of the CodeBuffer while code is being accumulated:

<span>代码缓存(3)</span> 

When the code and relocations are copied to the code cache,the empty parts of each section are removed, and everything is copied into contiguous locations.

CodeSection类的定义如下:

源代码位置:share/vm/asm/codeBuffer.hpp

// This class represents a stream of code and associated relocations.
// There are a few in each CodeBuffer.
// They are filled concurrently, and concatenated 使连接(连续,衔接)起来;连锁;串级; at the end.
class CodeSection VALUE_OBJ_CLASS_SPEC {
   //  ...
}

CodeSection的结构如下: 

<span>代码缓存(3)</span>

The _end (resp. _limit) pointer refers to the first unused (resp. unallocated) byte.

 

3、CodeCache::initialize()

在CodeCache::initialize()方法中调用的icache_init()方法的实现如下:

// For init.cpp
void icache_init() {
   ICache::initialize();
}

ICache::initialize()方法会调用AbstractICache类中的初始化方法,因为ICache类继承了AbstractICache类,如下:

void AbstractICache::initialize() {
  // Making this stub must be FIRST use of assembler
  ResourceMark rm;

  BufferBlob* b = BufferBlob::create("flush_icache_stub", ICache::stub_size);
  CodeBuffer  c(b); // CodeBuffer中有BufferBlob的引用

  ICacheStubGenerator g(&c);
  g.generate_icache_flush(&_flush_icache_stub);

  // The first use of flush_icache_stub must apply it to itself.
  // The StubCodeMark destructor in generate_icache_flush will
  // call Assembler::flush, which in turn will call invalidate_range,
  // which will in turn call the flush stub.  Thus we don't need an
  // explicit call to invalidate_range here.  This assumption is
  // checked in invalidate_range.
}

CodeBuffer的对象c根据BufferBlob的属性进行初始化。调用BufferBlob::create()方法创建一个BufferBlob对象。方法的实现如下:

BufferBlob* BufferBlob::create(const char* name, int buffer_size) {

  BufferBlob*    blob = NULL;
  unsigned int   size = sizeof(BufferBlob);

  // align the size to CodeEntryAlignment
  size = align_code_offset(size);
  size += round_to(buffer_size, oopSize); // oopSize对于是一个指针的宽度,在64位上就是8

  assert(name != NULL, "must provide a name");
  {
     MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
     blob = new (size) BufferBlob(name, size);
  }
  // Track memory usage statistic after releasing CodeCache_lock
  MemoryService::track_code_cache_memory_usage();

  return blob;
}

调用的align_code_offset()方法的实现如下:

unsigned int align_code_offset(int offset) {
  // align the size to CodeEntryAlignment
  return
    (
    	(
    	   offset + (int)CodeHeap::header_size() +  (CodeEntryAlignment-1)
	)
	&
	~(CodeEntryAlignment-1)
    )
    - (int)CodeHeap::header_size();
}

在BufferBlob::create()方法中创建BuferBlob对象时,调用new重载运算符和构造函数,如下:

void* BufferBlob::operator new(size_t s, unsigned size, bool is_critical) throw() {
  void* p = CodeCache::allocate(size, is_critical);
  return p;
}
BufferBlob::BufferBlob(const char* name, int size)
       : CodeBlob(
		name,
		sizeof(BufferBlob),
		size,
		CodeOffsets::frame_never_safe, /*locs_size:*/
		0
       )
{}

可以看到,会调用CodeCache::allocate()方法,这个方法在之前已经介绍过,这里不再介绍。

ICacheStubGenerator对象g在初始化时会初始化MacroAssembler,里面有_code_section属性,调用CodeBuffer的insts()方法获取CodeSection对象进行初始化。

class ICacheStubGenerator : public StubCodeGenerator {
 public:
  ICacheStubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} // 调用StubCodeGenerator类的构造函数
  // ...
}

class StubCodeGenerator: public StackObj {
 protected:
  MacroAssembler*  _masm; // 用来生成汇编代码

  StubCodeDesc* _first_stub;
  StubCodeDesc* _last_stub;
  // ...
}

StubCodeGenerator::StubCodeGenerator(CodeBuffer* code, bool print_code) {
  // 构造一个新的MacroAssembler实例
  _masm = new MacroAssembler(code);
  _first_stub = _last_stub = NULL;
  _print_code = print_code;
}

会创建MacroAssembler对象,如下:

MacroAssembler(CodeBuffer* code) : Assembler(code) {} // 调用另外的构造函数进行初始化

Assembler(CodeBuffer* code) : AbstractAssembler(code) {}


// The AbstractAssembler is generating code into a CodeBuffer. To make code generation faster,
// the assembler keeps a copy of the code buffers boundaries & modifies them when
// emitting bytes rather than using the code buffers accessor functions all the time.
// The code buffer is updated via set_code_end(...) after emitting a whole instruction.
AbstractAssembler::AbstractAssembler(CodeBuffer* code) {
  if (code == NULL)
	  return;
  CodeSection* cs = code->insts(); // 获取CodeBuffer中的指令部分insts
  cs->clear_mark();   // new assembler kills old mark

  _code_section = cs;
  _oop_recorder= code->oop_recorder();
}

ICache类的定义如下:

// Interface for updating the instruction cache.  Whenever the VM modifies
// code, part of the processor instruction cache potentially has to be flushed.

// On the x86, this is a no-op -- the I-cache is guaranteed to be consistent
// after the next jump, and the VM never modifies instructions directly ahead
// of the instruction fetch path.

// [phh] It's not clear that the above comment is correct, because on an MP
// system where the dcaches are not snooped, only the thread doing the invalidate
// will see the update.  Even in the snooped case, a memory fence would be
// necessary if stores weren't ordered.  Fortunately, they are on all known
// x86 implementations.

class ICache : public AbstractICache {
    // ...
}

AbstractICache类的说明如下:

// Interface for updating the instruction cache.  Whenever the VM modifies
// code, part of the processor instruction cache potentially has to be flushed.

// Default implementation is in icache.cpp, and can be hidden per-platform.
// Most platforms must provide only ICacheStubGenerator::generate_icache_flush().
// Platforms that don't require icache flushing can just nullify the public
// members of AbstractICache in their ICache class.  AbstractICache should never
// be referenced other than by deriving the ICache class from it.
//
// The code for the ICache class and for generate_icache_flush() must be in
// architecture-specific files, i.e., icache_<arch>.hpp/.cpp

class AbstractICache : AllStatic {
   ...
} 

CodeBuffer类的构造函数如下:

// External buffer, in a predefined CodeBlob.
// Important: The code_start must be taken exactly, and not realigned.
CodeBuffer::CodeBuffer(CodeBlob* blob) {
  initialize_misc("static buffer");
  initialize(blob->content_begin(), blob->content_size());
  verify_section_allocation();
}

 

 

4、JVM选项

这里列出一些和代码缓存相关的JVM选项。我想读了这篇文章前面的内容,应该很容易理解这些JVM选项的意义。

  • -XX:InitialCodeCacheSize:设置代码缓存的初始大小,这个参数在intel处理器下,在client编译器模式下是160KB,而在server编译器模式下是2496KB;
  • -XX:ReservedCodeCacheSize:设置代码缓存的大小;
  • -XX:+UseCodeCacheFlushing:当代码缓存满了的时候,让JVM换出一部分缓存以容纳新编译的代码。在默认情况下,这个选项是关闭的。这意味着,在代码缓存满了的时候,JVM会切换到纯解释器模式,这对于性能来说,可以说是毁灭性的影响;
  • -XX:NmethodSweepCheckInterval:设置清理缓存的时间间隔;
  • -XX:+DontCompileHugeMethods:默认不对大方法进行JIT编译;
  • -XX:HugeMethodLimit: 默认值是8000,遗憾的是,在产品环境下,该值不允许被修改;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/2676.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • javaSocket传输模式

    javaSocket传输模式Sockets有两种主要的操作方式:面向连接的和无连接的.面向连接的sockets操作就像一部电话,他们必须建立一个连接和一人呼叫.所有的事情在到达时的顺序与它们出发时的顺序时一样.无连接的sockets操作就像是一个邮件投递,,没有什么保证,多个邮件可能在到达时的顺序与出发时的顺序不一样.到底用哪种模式是邮应用程序的需要决定的.如果可靠性更重要的话,用面向连接的操作会好一些.比如文件服务器需要他们

  • 机器人视觉软件工程师_机器视觉工程师能干到多少岁

    机器人视觉软件工程师_机器视觉工程师能干到多少岁  大家好,本人是刚刚入职的视觉工程师,现在已经一年了,也给大家分享一下在这一段时间里,我做了什么,以及学到了什么。对了,虽然我只做了两个月的视觉工程师,但是我已经连续写了12年的日记了,我想把这个好习惯一直延续下去,也算是做个记录。可能没有人看,只做个人消遣吧!2020.9.15  先讲一下我的个人情况吧,大学是在湖南省湘潭市读的,在大四的时候遇到了一个好的老师,领我上了计算机视觉的末班车,为我拨开本来迷茫如雾的未来,在毕业后只身踏入计算机视觉漫漫长征路。其实坦白来讲,我是打算在毕业后做一名

  • Cooley-Tukey算法 (蝶形算法)

    Cooley-Tukey算法 (蝶形算法)

  • 茂名重力石化装备股份公司_恒力石化二期项目批复

    茂名重力石化装备股份公司_恒力石化二期项目批复目前来看,广东省已经拥有诸多国外化工巨头、大型民营炼化企业和不少国企的炼化项目,成为很多石化企业首选的项目落地基地。“石化业高质量发展看广东”,已经逐渐明朗。今年3月31日,广东省发展改革委官网公布《广东省2021年重点建设项目计划》。在2021年重点项目名单中,广东共安排省重点项目1395个,总投资达7.28万亿元,年度计划投资8000亿元。其中新开工项目有3个,总投资约267亿元,年度投资约60.5亿元,分别是恒力石化(惠州)PTA项目、东华能源(茂名)烷烃资源综合利用项目.

    2022年10月10日
  • 串口通信的三种方式(查询、中断、DMA)

    串口通信的三种方式(查询、中断、DMA)

  • QCustomPlot的基本使用[通俗易懂]

    QCustomPlot的基本使用[通俗易懂]QCustomPlot是QT下一个方便易用的绘图工具,只有两个文件qcustomplot.h和qcustomplot.cpp组成。源文件和使用文档可从官方网站下载。官方网站:http://www.qcustomplot.com/下面介绍下基本使用:1、将qcustomplot.cpp和qcustomplot.h拷贝到工程目录下,并在工程中添加文件。并在工程的pro文

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号