代码缓存(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,遗憾的是,在产品环境下,该值不允许被修改;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 雷电模拟器opengl版本过低_tx模拟器安卓

    雷电模拟器opengl版本过低_tx模拟器安卓上图说明你的显卡暂不支持模拟器,遇到这个问题,主要是3种原因:1、你的电脑没有显卡解决方案:这个问题若不换电脑硬件是暂时无解的,只能等待我们模拟器支持集成显卡了2、你的电脑显卡确实不支持OpenGL2.0解决方案:这个问题只能通过更换显卡来解决了上图说明你的显卡暂不支持模拟器,遇到这个问题,主要是3种原因:1、你的电脑没有显卡解决方案:这个问题若不换电脑硬件是暂时无解的,只能等待我们模拟器…

    2022年10月8日
  • java定义数组_java中数组的三种定义方式_java中数组的定义及使用方法(推荐)…

    java定义数组_java中数组的三种定义方式_java中数组的定义及使用方法(推荐)…描述java中数组的三种定义方式java中,数组是一种很常用的工具,今天我们来说说数组怎么定义[java]viewplaincopy/***数组的三种定义方法*1.数组类型[]数组名=new数组类型[数组长度];*2.数组类型[]数组名={数组0,数组1,数组2,数组3,。。。。};*3.数组类型[]数组名=new数组类型[]{数组0,数组1,数组2,。。。};**/p…

    2022年7月8日
  • springboot配置文件的属性集

    springboot配置文件的属性集springboot配置文件的属性集

    2022年4月22日
  • js面试笔试–prototype详解

    js面试笔试–prototype详解对JavaScript中原型模式的理解一:什么是原型对象?有什么优点?   简单的来说,无论何时,我们创建的每一个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象包含了通过调用该构造函数所创建的对象共享的属性和方法。其实我们平常的叫法就是指:prototype就是通过该构造函数创建的某个实例的原型对象,但是其实prototype是每个构造函数的属性而已,只能说…

    2022年7月22日
  • iframe高度自适应_读取跨域iframe

    iframe高度自适应_读取跨域iframe采用JavaScript来控制iframe元素的高度是iframe高度自适应的关键,同时由于JavaScript对不同域名下权限的控制,引发出同域、跨域两种情况。由于客户端js使用浏览器的同源安全策略,跨域情况下,被嵌套页面如果想要获取和修改父页面的DOM属性会出现权限不足的情况,提示错误:Permissiondeniedtoaccessproperty’document’。这是

    2022年10月12日
  • docker(11)Dockerfile 中的COPY与ADD 命令「建议收藏」

    docker(11)Dockerfile 中的COPY与ADD 命令「建议收藏」前言Dockerfile中提供了两个非常相似的命令COPY和ADD,本文尝试解释这两个命令的基本功能,以及其异同点,然后总结其各自适合的应用场景。Build上下文的概念在使用dock

    2022年7月28日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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