某些指令需要创建某些实例,如下:
指令对应的DataLayout._struct._tag值_checkcast、_instanceof、_aastore
receiver_type_data_tag
bit_data_tag
_invokespecial、_invokestaticcall_type_data_tag
counter_data_tag
_goto、_goto_w、_jsr、_jsr_wjump_data_tag_invokevirtual、_invokeinterfacevirtual_call_type_data_tag
virtual_call_data_tag
_invokedynamiccall_type_data_tag
counter_data_tag
_retret_data_tag_ifeq、_ifne、_iflt、_ifge、_ifgt、_ifle、
_if_icmpeq、_if_icmpne、_if_icmplt、_if_icmpge、
_if_icmpgt、_if_icmple、_if_acmpeq、_if_acmpne、
_ifnull、_ifnonnull
branch_data_tag_lookupswitch、_tableswitchmulti_branch_data_tag如上表格的DataLayout._struct._tag属性的值可能有多个可选,这样最终会根据实际情况选择一个。
之前介绍过,通过MethodData能够详细记录字节码的执行情况。如在回边计数中,向MethodData中的JumpData::taken_off_set处存储跳转次数,通过JumpData::displacement_off_set处的值来更新栈帧中的interpreter_frame_mdx_offset处的值,那么MethodData中是如何存储这些数据的,这些数据是什么意思,存储这些数据有什么作用呢?要理解这些,我们需要首先介绍下ProfileData和DataLayout。
在MethodData中声明与统计相关的字段如下:
?
class
MethodData :
public
Metadata {
// 这个值是为了更快的通过字节码下标索引找到对应data的指针(调用bci_to_dp()函数)
// 或通过字节码下标索引找到对应的ProfileData(调用bci_to_data()函数)
int
_hint_di;
// 对回边和方法调用进行计数
InvocationCounter _invocation_counter;
InvocationCounter _backedge_counter;
// 开始计数时的初始值
int
_invocation_counter_start;
int
_backedge_counter_start;
int
_data_size;
// 形参的data索引,如果没有,此值为-1
int
_parameters_type_data_di;
// 数据的起始地址
intptr_t _data[1];
// ...
}
其中的_data属性的值很重要。其与ProfileData与DataLayout类的关系如下:
通过上图能够看到,除去MethodData本身占用的内存外,还会额外分配不少内存,这些内存都会用来存储一些统计相关信息,而这些数据区域可被看成元素类型为intptr_t的数组,而MethodData::_data是这个数组的首地址。由于数组中会存储一些数据,这些数据的长度不一,例如有的可能需要占用数组中连续2个slot,有的可能是3个,但是仍然可以通过数组的下标标示出数据的首地址。
存储的数据的布局由DataLayout决定,按DataLayout布局的数据都对应着ProfileData,所以可以通过ProfileData操作数据。
定义的ProfileData类的定义如下:
?
class
ProfileData :
public
ResourceObj {
DataLayout* _data;
// 规定了数据的存储格式
// ...
}
如上类有非常多的子类,如下图所示。
这些具体的子类的布局和数据大小都不一样,但是同一个子类的不同实例大小和数据布局是一样的,如果我们能知道某个ProfileData的首地址,那么就能准确无误的读取出具体子类实例的数据。
DataLayout类的定义如下:
?
class
DataLayout VALUE_OBJ_CLASS_SPEC {
private
:
union {
// intptr_t类型占用4个字节
intptr_t _bits;
struct
{
u1 _tag;
// flags的格式为[ recompile:1 | reason:3 | flags:4]
u1 _flags;
u2 _bci;
} _struct;
} _header;
// 可以有许多个cells,首个cell的地址通过如下的_cells属性保存,
// 具体的cells数组的大小还要根据具体的ProfileData来决定
intptr_t _cells[1];
// ...
}
如上类定义了每个ProfileData实例的数据布局。首先就是_tag属性,这个属性决定着ProfileData,也就是这一部分数据对应着哪个具体的ProfileData的子类,不同的子类其_cells数组的大小不同;_bci表示字节码索引,对应的当前的_cells中存储的就是关于这个字节码索引对应的字节码的一些统计相关信息。
_tag属性的值由枚举类定义,如下:
?
enum
{
no_tag,
bit_data_tag,
counter_data_tag,
jump_data_tag,
receiver_type_data_tag,
virtual_call_data_tag,
ret_data_tag,
branch_data_tag,
multi_branch_data_tag,
arg_info_data_tag,
call_type_data_tag,
virtual_call_type_data_tag,
parameters_type_data_tag
};
如上枚举类的常量值和ProfileData的具体子类有某种对应关系,通过data_in()函数就能看到这种对应关系。函数的实现如下:
?
ProfileData* DataLayout::data_in() {
switch
(tag()) {
case
DataLayout::no_tag:
default
:
ShouldNotReachHere();
return
NULL;
case
DataLayout::bit_data_tag:
return
new
BitData(
this
);
case
DataLayout::counter_data_tag:
return
new
CounterData(
this
);
case
DataLayout::jump_data_tag:
return
new
JumpData(
this
);
case
DataLayout::receiver_type_data_tag:
return
new
ReceiverTypeData(
this
);
case
DataLayout::virtual_call_data_tag:
return
new
VirtualCallData(
this
);
case
DataLayout::ret_data_tag:
return
new
RetData(
this
);
case
DataLayout::branch_data_tag:
return
new
BranchData(
this
);
case
DataLayout::multi_branch_data_tag:
return
new
MultiBranchData(
this
);
case
DataLayout::arg_info_data_tag:
return
new
ArgInfoData(
this
);
case
DataLayout::call_type_data_tag:
return
new
CallTypeData(
this
);
case
DataLayout::virtual_call_type_data_tag:
return
new
VirtualCallTypeData(
this
);
case
DataLayout::parameters_type_data_tag:
return
new
ParametersTypeData(
this
);
};
}
如果要从MethodData::_data中获取某个具体的ProfileData实例,可以调用MethodData::data_at()函数,此函数的实现如下:
?
ProfileData* MethodData::data_at(
int
data_index)
const
{
DataLayout* data_layout = data_layout_at(data_index);
return
data_layout->data_in();
}
DataLayout* data_layout_at(
int
data_index)
const
{
// 由于_data所指向的元素类型为intptr_t,占用8字节,所以加整数后每次会移动8字节
return
(DataLayout*) ( ((address)_data) + data_index );
}
在调用MethodData::data_at()函数时,传递的data_index属性的值就是前面提到的数据区域,这些数据区域可被看成元素类型为intptr_t的数组,所以可以通过数组下标索引来指示出某个具体数据的首地址。
调用data_layout_at()获取到DataLayout指针后,可以直接根据布局中的_tag属性转换为ProfileData实例,这样就可以通过ProfileData实例操作_cells中存储的数据了。
我们之前介绍过,除了MethodData本身占用的内存之外,还会分配出不少内存,我们暂时叫数据区域,这一块区域可能需要存储最多4种类型的数据,我们从为MethodData开辟内存空间的计算方式和在构造函数的初始化过程中就能看出来。下面分别介绍一下为MethoData开辟存储空间和调用MethodData构造函数中的初始化过程。
1、为MethodData开辟存储空间
在分配MethodData实例时,需要调用如下函数计算分配的内存空间的大小:
?
int
MethodData::compute_allocation_size_in_words(methodHandle method) {
int
byte_size = compute_allocation_size_in_bytes(method);
int
word_size = align_size_up(byte_size, BytesPerWord) / BytesPerWord;
return
align_object_size(word_size);
// 以字为单位的内存占用大小
}
int
MethodData::compute_allocation_size_in_bytes(methodHandle method) {
// 第1部分
int
data_size = 0;
BytecodeStream stream(method);
Bytecodes::Code c;
int
empty_bc_count = 0;
// 统计出方法中的哪些字节码不需要统计信息,也就是不会有对应的ProfileData实例
while
((c = stream.next()) >= 0) {
int
size_in_bytes = compute_data_size(&stream);
data_size += size_in_bytes;
if
(size_in_bytes == 0)
empty_bc_count += 1;
}
int
object_size = in_bytes(data_offset()) + data_size;
// 第2部分
// Add some extra DataLayout cells (at least one) to track stray traps.
int
extra_data_count = compute_extra_data_count(data_size, empty_bc_count);
object_size += extra_data_count * DataLayout::compute_size_in_bytes(0);
// 第3部分
// Add a cell to record information about modified arguments.
int
arg_size = method->size_of_parameters();
object_size += DataLayout::compute_size_in_bytes(arg_size+1);
// 第4部分
// Reserve room for an area of the MDO dedicated to profiling of parameters
int
args_cell = ParametersTypeData::compute_cell_count(method());
if
(args_cell > 0) {
object_size += DataLayout::compute_size_in_bytes(args_cell);
}
return
object_size;
}
在创建MethodData实例时,除了要开辟自身占用的内存空间外,还要额外开辟另外的4部分空间,如下图所示。
下面详细介绍一下这几部分实现。
第1部分
对Java方法中的所有字节码调用compute_data_size()函数计算大小,如下:
?
// 为当前的字节码计算需要的统计信息占用的内存大小
int
MethodData::compute_data_size(BytecodeStream* stream) {
// 有些字节码需要对应的ProfileData,返回所有需要的ProfileData需要占用的内存大小
int
cell_count = bytecode_cell_count(stream->code());
if
(cell_count == no_profile_data) {
return
0;
}
// 当为invokestatic、invokeinterface、invokedynamic、tableswitch和lookupswitch时
// 调用bytecode_cell_count()函数可能会返回variable_cell_count,因为这几个字节码指令
// 需要的ProfileData的大小是可变的,需要下面的逻辑继续进行计算
if
(cell_count == variable_cell_count) {
switch
(stream->code()) {
case
Bytecodes::_lookupswitch:
case
Bytecodes::_tableswitch:
cell_count = MultiBranchData::compute_cell_count(stream);
break
;
case
Bytecodes::_invokespecial:
case
Bytecodes::_invokestatic:
case
Bytecodes::_invokedynamic:
if
(profile_arguments_for_invoke(stream->method(), stream->bci()) ||
profile_return_for_invoke(stream->method(), stream->bci())) {
cell_count = CallTypeData::compute_cell_count(stream);
}
else
{
cell_count = CounterData::static_cell_count();
}
break
;
// 方法动态分派指令invokevirtual和invokeinterface
case
Bytecodes::_invokevirtual:
case
Bytecodes::_invokeinterface: {
if
(profile_arguments_for_invoke(stream->method(), stream->bci()) ||
profile_return_for_invoke(stream->method(), stream->bci())) {
cell_count = VirtualCallTypeData::compute_cell_count(stream);
}
else
{
cell_count = VirtualCallData::static_cell_count();
}
break
;
}
default
:
fatal(
"unexpected bytecode for var length profile data"
);
}
}
return
DataLayout::compute_size_in_bytes(cell_count);
}
调用的bytecode_cell_count()函数的实现如下:
?
int
MethodData::bytecode_cell_count(Bytecodes::Code code) {
switch
(code) {
case
Bytecodes::_checkcast:
case
Bytecodes::_instanceof:
case
Bytecodes::_aastore:
if
(TypeProfileCasts) {
// 默认会走的分支
return
ReceiverTypeData::static_cell_count();
}
else
{
return
BitData::static_cell_count();
}
case
Bytecodes::_invokespecial:
case
Bytecodes::_invokestatic:
if
(MethodData::profile_arguments() || MethodData::profile_return()) {
return
variable_cell_count;
}
else
{
return
CounterData::static_cell_count();
}
case
Bytecodes::_goto:
case
Bytecodes::_goto_w:
case
Bytecodes::_jsr:
case
Bytecodes::_jsr_w:
return
JumpData::static_cell_count();
case
Bytecodes::_invokevirtual:
case
Bytecodes::_invokeinterface:
if
(MethodData::profile_arguments() || MethodData::profile_return()) {
return
variable_cell_count;
}
else
{
// 默认会走的分支
return
VirtualCallData::static_cell_count();
}
case
Bytecodes::_invokedynamic:
if
(MethodData::profile_arguments() || MethodData::profile_return()) {
return
variable_cell_count;
}
else
{
// 默认会走的分支
return
CounterData::static_cell_count();
}
case
Bytecodes::_ret:
return
RetData::static_cell_count();
case
Bytecodes::_ifeq:
case
Bytecodes::_ifne:
case
Bytecodes::_iflt:
case
Bytecodes::_ifge:
case
Bytecodes::_ifgt:
case
Bytecodes::_ifle:
case
Bytecodes::_if_icmpeq:
case
Bytecodes::_if_icmpne:
case
Bytecodes::_if_icmplt:
case
Bytecodes::_if_icmpge:
case
Bytecodes::_if_icmpgt:
case
Bytecodes::_if_icmple:
case
Bytecodes::_if_acmpeq:
case
Bytecodes::_if_acmpne:
case
Bytecodes::_ifnull:
case
Bytecodes::_ifnonnull:
return
BranchData::static_cell_count();
case
Bytecodes::_lookupswitch:
case
Bytecodes::_tableswitch:
return
variable_cell_count;
}
return
no_profile_data;
}
对于分支的指令才可能开辟空间,然后开辟出空间来存储统计信息。当为invokestatic、invokeinterface、invokedynamic、tableswitch和lookupswitch时,调用bytecode_cell_count()函数可能会返回variable_cell_count,因为这几个字节码指令需要的ProfileData的大小是可变的,需要在MethodData::compute_data_size()函数中继续进行计算。
对于方法调用来说,有时候还需要统计方法的参数或返回值,MethodData::profile_arguments()函数的实现如下:
?
bool
MethodData::profile_arguments() {
// 函数默认返回false
return
profile_arguments_flag() > no_type_profile &&
profile_arguments_flag() <= type_profile_all;
}
int
MethodData::profile_arguments_flag() {
return
TypeProfileLevel % 10;
// TypeProfileLevel默认的值为0
}
int
MethodData::profile_return_flag() {
return
(TypeProfileLevel % 100) / 10;
// TypeProfileLevel的值为0
}
bool
MethodData::profile_return() {
// 函数默认返回false
return
profile_return_flag() > no_type_profile &&
profile_return_flag() <= type_profile_all;
}
其中的TypeProfilieLevel的值可指定为XYZ,其中的X、Y和Z的意思如下:
Z:在调用时的实参类型统计;
Y:在调用时的返回值类型统计;
X:形参的类型统计。
在每一位上都有3个值可以取,通过枚举类常量定义,如下:
?
enum
{
no_type_profile = 0,
type_profile_jsr292 = 1,
type_profile_all = 2
};
回看MethodData::compute_data_size()函数中计算invokestatic、invokeinterface、invokedynamic、tableswitch和lookupswitch需要的内存大小,如计算invokespecial的大小,如下:
?
static
int
compute_cell_count(BytecodeStream* stream) {
// 调用static_cell_count()的值为1
return
CounterData::static_cell_count() +
TypeEntriesAtCall::compute_cell_count(stream);
}
int
TypeEntriesAtCall::compute_cell_count(BytecodeStream* stream) {
Bytecode_invoke inv(stream->method(), stream->bci());
int
args_cell = 0;
// 同样会调用MethodData::profile_arguments()函数进行判断,默认会返回false
if
(arguments_profiling_enabled()) {
args_cell = TypeStackSlotEntries::compute_cell_count(inv.signature(),
false
, TypeProfileArgsLimit);
}
int
ret_cell = 0;
// 当调用MethodData::profile_return()函数返回true,并且返回类型为对象类型时需要统计信息时,if的body体会执行。不过默认条件下不会执行
if
(return_profiling_enabled() && (inv.result_type() == T_OBJECT || inv.result_type() == T_ARRAY)) {
ret_cell = ReturnTypeEntry::static_cell_count();
}
// 如果要记录统计信息,必须要有头
int
header_cell = 0;
if
(args_cell + ret_cell > 0) {
header_cell = header_cell_count();
}
return
header_cell + args_cell + ret_cell;
}
int
TypeStackSlotEntries::compute_cell_count(
Symbol* signature,
bool
include_receiver,
int
max
) {
// 当为实例方法时,还必须要考虑this参数
int
args_count = include_receiver ? 1 : 0;
ResourceMark rm;
SignatureStream ss(signature);
// 统计参数中对象类型的数量
args_count += ss.reference_parameter_count();
args_count = MIN2(args_count, max);
return
args_count * per_arg_cell_count;
}
MethodData::compute_data_size()函数中调用的DataLayout::compute_size_in_bytes()函数的实现如下:
?
static
int
compute_size_in_bytes(
int
cell_count) {
// 8 + cell_count * 8
return
header_size_in_bytes() + cell_count * cell_size;
}
计算出需要的字节大小。每个字节码需要的cell_count不同,这样DataLayout的大小就不同,最终对应的ProfileData实例的大小就不同。
第2部分
第2部分的实现代码如下:
?
int
MethodData::compute_extra_data_count(
int
data_size,
int
empty_bc_count) {
if
(ProfileTraps) {
// Assume that up to 3% of BCIs with no MDP will need to allocate one. 多于3%的、不需要MDP的BCIs将会分配data
int
extra_data_count = (
uint
)(empty_bc_count * 3) / 128 + 1;
// If the method is large, let the extra BCIs grow numerous (to ~1%).
int
one_percent_of_data = (
uint
)data_size / (DataLayout::header_size_in_bytes()*128);
if
(extra_data_count < one_percent_of_data){
extra_data_count = one_percent_of_data;
}
if
(extra_data_count > empty_bc_count){
extra_data_count = empty_bc_count;
// no need for more
}
return
extra_data_count;
}
else
{
return
0;
}
}
这一部分和逆优化相关。编译器可以根据概率选择一些大多数时候都能提升运行速度的优化手段,当激进优化的假设不成立,如加载了新类后类型继承结构出现变化、出现 “罕见陷阱”(Uncommon Trap)时可以通过逆优化(Deoptimization)退回到解释状态继续执行。如上的数据就是用来跟踪这些陷阱的。 现在还看不到应用,后面再详细介绍。
第4部分
第3部分非常简单,不介绍。直接介绍第4部分,第4部分的代码实现如下:
?
int
ParametersTypeData::compute_cell_count(Method* m) {
if
(!MethodData::profile_parameters_for_method(m)) {
return
0;
}
int
max = TypeProfileParmsLimit == -1 ? INT_MAX : TypeProfileParmsLimit;
// TypeProfileParmsLimit的默认值为2
int
obj_args = TypeStackSlotEntries::compute_cell_count(m->signature(), !m->is_static(), max);
if
(obj_args > 0) {
return
obj_args + 1;
// 1 cell for array len
}
return
0;
}
调用的TypeStackSlotEntries::compute_cell_count()函数计算出方法参数中的对象类型。不过默认情况下,这个函数返回的是0。
2、调用MethodData构造函数
通过上面开辟完MethodData内存空间后就需要调用MethodData构造函数了,构造函数的实现如下:
?
MethodData::MethodData(methodHandle method,
int
size, TRAPS) {
// ...
init();
// 第1部分
// 迭代字节码指令并分配和初始化对应的data cell
int
data_size = 0;
int
empty_bc_count = 0;
// 不需要data cell的字节码数量
_data[0] = 0;
BytecodeStream stream(method);
Bytecodes::Code c;
while
((c = stream.next()) >= 0) {
int
size_in_bytes = initialize_data(&stream, data_size);
data_size += size_in_bytes;
if
(size_in_bytes == 0)
empty_bc_count += 1;
}
_data_size = data_size;
int
object_size = in_bytes(data_offset()) + data_size;
// 跳过第2部分
int
extra_data_count = compute_extra_data_count(data_size, empty_bc_count);
int
extra_size = extra_data_count * DataLayout::compute_size_in_bytes(0);
DataLayout *dp = data_layout_at(data_size + extra_size);
// 第3部分
int
arg_size = method->size_of_parameters();
// 调用的函数的声明如下:
// DataLayout::initialize(u1 tag, u2 bci, int cell_count)
dp->initialize(DataLayout::arg_info_data_tag, 0, arg_size+1);
int
arg_data_size = DataLayout::compute_size_in_bytes(arg_size+1);
object_size += extra_size + arg_data_size;
// 第4部分,当方法没有形参时,_parameter_type_data_di的值为-1,否则为data index
int
args_cell = ParametersTypeData::compute_cell_count(method());
if
(args_cell > 0) {
object_size += DataLayout::compute_size_in_bytes(args_cell);
_parameters_type_data_di = data_size + extra_size + arg_data_size;
DataLayout *dp = data_layout_at(data_size + extra_size + arg_data_size);
dp->initialize(DataLayout::parameters_type_data_tag, 0, args_cell);
}
else
{
_parameters_type_data_di = -1;
}
// 这个值有利于我们通过字节码索引快速找到对应的data cell
_hint_di = first_di();
post_initialize(&stream);
set_size(object_size);
}
之前介绍过,在创建MethodData实例时,首先要开辟相关空间,而如上函数的几部分代码正好在初始化之前几个部分开辟出的空间。
第1部分
调用如下函数对字节码对应的data cell初始化,函数的实现如下:
?
// Initialize an individual data segment. Returns the size of
// the segment in bytes.
int
MethodData::initialize_data(
BytecodeStream* stream,
int
data_index
) {
int
cell_count = -1;
int
tag = DataLayout::no_tag;
DataLayout* data_layout = data_layout_at(data_index);
Bytecodes::Code c = stream->code();
switch
(c) {
case
Bytecodes::_checkcast:
case
Bytecodes::_instanceof:
case
Bytecodes::_aastore:
if
(TypeProfileCasts) {
// TypeProfileCasts默认的值为true
cell_count = ReceiverTypeData::static_cell_count();
// 值为5
tag = DataLayout::receiver_type_data_tag;
}
else
{
cell_count = BitData::static_cell_count();
// 值为0
tag = DataLayout::bit_data_tag;
}
break
;
case
Bytecodes::_invokespecial:
case
Bytecodes::_invokestatic: {
int
counter_data_cell_count = CounterData::static_cell_count();
// 值为1
if
(
profile_arguments_for_invoke(stream->method(), stream->bci()) ||
profile_return_for_invoke(stream->method(), stream->bci())
) {
cell_count = CallTypeData::compute_cell_count(stream);
}
else
{
cell_count = counter_data_cell_count;
}
if
(cell_count > counter_data_cell_count) {
tag = DataLayout::call_type_data_tag;
}
else
{
tag = DataLayout::counter_data_tag;
}
break
;
}
case
Bytecodes::_goto:
case
Bytecodes::_goto_w:
case
Bytecodes::_jsr:
case
Bytecodes::_jsr_w:
cell_count = JumpData::static_cell_count();
tag = DataLayout::jump_data_tag;
break
;
case
Bytecodes::_invokevirtual:
case
Bytecodes::_invokeinterface: {
int
virtual_call_data_cell_count = VirtualCallData::static_cell_count();
if
(
profile_arguments_for_invoke(stream->method(), stream->bci()) ||
profile_return_for_invoke(stream->method(), stream->bci())
) {
cell_count = VirtualCallTypeData::compute_cell_count(stream);
}
else
{
cell_count = virtual_call_data_cell_count;
}
if
(cell_count > virtual_call_data_cell_count) {
tag = DataLayout::virtual_call_type_data_tag;
}
else
{
tag = DataLayout::virtual_call_data_tag;
}
break
;
}
case
Bytecodes::_invokedynamic: {
// %%% should make a type profile for any invokedynamic that takes a ref argument
int
counter_data_cell_count = CounterData::static_cell_count();
if
(
profile_arguments_for_invoke(stream->method(), stream->bci()) ||
profile_return_for_invoke(stream->method(), stream->bci())
) {
cell_count = CallTypeData::compute_cell_count(stream);
}
else
{
cell_count = counter_data_cell_count;
}
if
(cell_count > counter_data_cell_count) {
tag = DataLayout::call_type_data_tag;
}
else
{
tag = DataLayout::counter_data_tag;
}
break
;
}
case
Bytecodes::_ret:
cell_count = RetData::static_cell_count();
// 值为7
tag = DataLayout::ret_data_tag;
break
;
case
Bytecodes::_ifeq:
case
Bytecodes::_ifne:
case
Bytecodes::_iflt:
case
Bytecodes::_ifge:
case
Bytecodes::_ifgt:
case
Bytecodes::_ifle:
case
Bytecodes::_if_icmpeq:
case
Bytecodes::_if_icmpne:
case
Bytecodes::_if_icmplt:
case
Bytecodes::_if_icmpge:
case
Bytecodes::_if_icmpgt:
case
Bytecodes::_if_icmple:
case
Bytecodes::_if_acmpeq:
case
Bytecodes::_if_acmpne:
case
Bytecodes::_ifnull:
case
Bytecodes::_ifnonnull:
cell_count = BranchData::static_cell_count();
// 值为3
tag = DataLayout::branch_data_tag;
break
;
case
Bytecodes::_lookupswitch:
case
Bytecodes::_tableswitch:
cell_count = MultiBranchData::compute_cell_count(stream);
tag = DataLayout::multi_branch_data_tag;
break
;
}
if
(cell_count >= 0) {
data_layout->initialize(tag, stream->bci(), cell_count);
// 如下函数的计算方式为:4 + cell_count * 4
return
DataLayout::compute_size_in_bytes(cell_count);
}
else
{
assert(!bytecode_has_profile(c),
"agree w/ !BHP"
);
return
0;
}
}
函数初始化对应的data cell并返回data cell的大小。
第3与第4部分
DataLayout::initialize()函数的实现如下:
?
// 初始化一般性的属性,对于一些特定属性的初始化,通过调用
// ProfileData::post_initialize()函数完成
void
DataLayout::initialize(u1 tag, u2 bci,
int
cell_count) {
_header._bits = (intptr_t)0;
_header._struct._tag = tag;
_header._struct._bci = bci;
for
(
int
i = 0; i < cell_count; i++) {
set_cell_at(i, (intptr_t)0);
}
if
(needs_array_len(tag)) {
set_cell_at(ArrayData::array_len_off_set, cell_count - 1);
// -1 for header.
}
if
(tag == call_type_data_tag) {
CallTypeData::initialize(
this
, cell_count);
}
else
if
(tag == virtual_call_type_data_tag) {
VirtualCallTypeData::initialize(
this
, cell_count);
}
}
bool
DataLayout::needs_array_len(u1 tag) {
return
(tag == multi_branch_data_tag) ||
(tag == arg_info_data_tag) ||
(tag == parameters_type_data_tag);
}
void
set_cell_at(
int
index, intptr_t value) {
_cells[index] = value;
}
可以看到为DataLayout中的各个属性赋值的具体实现。不过这里只会对_cells中的部分slot进行赋值,具体这些slot中存储什么样的值,还要看具体存储的ProfileData的具体类型。我们在下一篇给一个具体的实例。
可以根据《深入解析HotSpot》来研究Template::branch()的实现。
即使解释器通过循环体的回边探测到一个热点方法,并对该方法进行了编译,但是main方法只执行一次,被编译后的main方法根本没有执行的机会。为了解决这个问题,需要一种机制:在运行main方法的过程中(而非运行main()方法之后)使用编译后的方法替换当前执行,这样的机制称为OSR。OSR用于方法从低层次向高层次执行变换。发生OSR的时机是遇到回边字节码,而回边又是在branch中体现的。
公众号 深入剖析Java虚拟机HotSpot 已经更新虚拟机源代码剖析相关文章到60+,欢迎关注,如果有任何问题,可加作者微信mazhimazh,拉你入虚拟机群交流