Skip to main content

volatile java 实现

· 17 min read

背景

了解java volatile实现

实现

核心在这里: is_volatile_shift

void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteControl rc) {
transition(vtos, vtos);

const Register cache = rcx;
const Register index = rdx;
const Register obj = rcx;
const Register off = rbx;
const Register flags = rax;

resolve_cache_and_index(byte_no, cache, index, sizeof(u2));
jvmti_post_field_mod(cache, index, is_static);
load_field_cp_cache_entry(obj, cache, index, off, flags, is_static);

Label notVolatile, Done;
__ movl(rdx, flags);
__ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift); // 右移volatile 标志位 , 然后如果是volatile ,则走到不太分支
__ andl(rdx, 0x1);

// Check for volatile store
__ testl(rdx, rdx);
__ jcc(Assembler::zero, notVolatile);

putfield_or_static_helper(byte_no, is_static, rc, obj, off, flags);
volatile_barrier(Assembler::Membar_mask_bits(Assembler::StoreLoad |
Assembler::StoreStore));
__ jmp(Done);
__ bind(notVolatile);

putfield_or_static_helper(byte_no, is_static, rc, obj, off, flags);

__ bind(Done);
}

这是具体的代码:

// ----------------------------------------------------------------------------
// Volatile variables demand their effects be made known to all CPU's
// in order. Store buffers on most chips allow reads & writes to
// reorder; the JMM's ReadAfterWrite.java test fails in -Xint mode
// without some kind of memory barrier (i.e., it's not sufficient that
// the interpreter does not reorder volatile references, the hardware
// also must not reorder them).
//
// According to the new Java Memory Model (JMM):
// (1) All volatiles are serialized wrt to each other. ALSO reads &
// writes act as aquire & release, so:
// (2) A read cannot let unrelated NON-volatile memory refs that
// happen after the read float up to before the read. It's OK for
// non-volatile memory refs that happen before the volatile read to
// float down below it.
// (3) Similar a volatile write cannot let unrelated NON-volatile
// memory refs that happen BEFORE the write float down to after the
// write. It's OK for non-volatile memory refs that happen after the
// volatile write to float up before it.
//
// We only put in barriers around volatile refs (they are expensive),
// not _between_ memory refs (that would require us to track the
// flavor of the previous memory refs). Requirements (2) and (3)
// require some barriers before volatile stores and after volatile
// loads. These nearly cover requirement (1) but miss the
// volatile-store-volatile-load case. This final case is placed after
// volatile-stores although it could just as well go before
// volatile-loads.

void TemplateTable::volatile_barrier(Assembler::Membar_mask_bits order_constraint ) {
// Helper function to insert a is-volatile test and memory barrier
__ membar(order_constraint);
}

还有这段:

ConstantPoolCache 看上去是运行时从.class文件读取的内容, 这里就有volatile的标志位,我们看看是在哪里设置这个值ConstantPoolCacheEntry::is_volatile_shift

// The ConstantPoolCache is not a cache! It is the resolution table that the
// interpreter uses to avoid going into the runtime and a way to access resolved
// values.

// A ConstantPoolCacheEntry describes an individual entry of the constant
// pool cache. There's 2 principal kinds of entries: field entries for in-
// stance & static field access, and method entries for invokes. Some of
// the entry layout is shared and looks as follows:
//
// bit number |31 0|
// bit length |-8--|-8--|---16----|
// --------------------------------
// _indices [ b2 | b1 | index ] index = constant_pool_index
// _f1 [ entry specific ] metadata ptr (method or klass)
// _f2 [ entry specific ] vtable or res_ref index, or vfinal method ptr
// _flags [tos|0|F=1|0|0|0|f|v|0 |0000|field_index] (for field entries)
// bit length [ 4 |1| 1 |1|1|1|1|1|1 |1 |-3-|----16-----]
// _flags [tos|0|F=0|S|A|I|f|0|vf|indy_rf|000|00000|psize] (for method entries)
// bit length [ 4 |1| 1 |1|1|1|1|1|1 |-4--|--8--|--8--]

// --------------------------------
//
// with:
// index = original constant pool index
// b1 = bytecode 1
// b2 = bytecode 2
// psize = parameters size (method entries only)
// field_index = index into field information in holder InstanceKlass
// The index max is 0xffff (max number of fields in constant pool)
// and is multiplied by (InstanceKlass::next_offset) when accessing.
// tos = TosState
// F = the entry is for a field (or F=0 for a method)
// A = call site has an appendix argument (loaded from resolved references)
// I = interface call is forced virtual (must use a vtable index or vfinal)
// f = field or method is final
// v = field is volatile
// vf = virtual but final (method entries only: is_vfinal())
// indy_rf = call site specifier method resolution failed
//
// The flags after TosState have the following interpretation:
// bit 27: 0 for fields, 1 for methods
// f flag true if field is marked final
// v flag true if field is volatile (only for fields)
// f2 flag true if f2 contains an oop (e.g., virtual final method)
// fv flag true if invokeinterface used for method in class Object
//
// The flags 31, 30, 29, 28 together build a 4 bit number 0 to 16 with the
// following mapping to the TosState states:
//
// btos: 0
// ztos: 1
// ctos: 2
// stos: 3
// itos: 4
// ltos: 5
// ftos: 6
// dtos: 7
// atos: 8
// vtos: 9
//
// Entry specific: field entries:
// _indices = get (b1 section) and put (b2 section) bytecodes, original constant pool index
// _f1 = field holder (as a java.lang.Class, not a Klass*)
// _f2 = field offset in bytes
// _flags = field type information, original FieldInfo index in field holder
// (field_index section)
//
// Entry specific: method entries:
// _indices = invoke code for f1 (b1 section), invoke code for f2 (b2 section),
// original constant pool index
// _f1 = Method* for non-virtual calls, unused by virtual calls.
// for interface calls, which are essentially virtual but need a klass,
// contains Klass* for the corresponding interface.
// for invokedynamic and invokehandle, f1 contains the adapter method which
// manages the actual call. The appendix is stored in the ConstantPool
// resolved_references array.
// (upcoming metadata changes will move the appendix to a separate array)
// _f2 = vtable/itable index (or final Method*) for virtual calls only,
// unused by non-virtual. The is_vfinal flag indicates this is a
// method pointer for a final method, not an index.
// _flags = has local signature (MHs and indy),
// virtual final bit (vfinal),
// parameter size (psize section)
//
// Note: invokevirtual & invokespecial bytecodes can share the same constant
// pool entry and thus the same constant pool cache entry. All invoke
// bytecodes but invokevirtual use only _f1 and the corresponding b1
// bytecode, while invokevirtual uses only _f2 and the corresponding
// b2 bytecode. The value of _flags is shared for both types of entries.
//
// The fields are volatile so that they are stored in the order written in the
// source code. The _indices field with the bytecode must be written last.

class CallInfo;

class ConstantPoolCacheEntry {

然后我们看看设置值的地方在这里

 //源码地址   src/hotspot/share/oops/cpCache.cpp

// Note that concurrent update of both bytecodes can leave one of them
// reset to zero. This is harmless; the interpreter will simply re-resolve
// the damaged entry. More seriously, the memory synchronization is needed
// to flush other fields (f1, f2) completely to memory before the bytecodes
// are updated, lest other processors see a non-zero bytecode but zero f1/f2.
void ConstantPoolCacheEntry::set_field(Bytecodes::Code get_code,
Bytecodes::Code put_code,
Klass* field_holder,
int field_index,
int field_offset,
TosState field_type,
bool is_final,
bool is_volatile) {
set_f1(field_holder);
set_f2(field_offset);
assert((field_index & field_index_mask) == field_index,
"field index does not fit in low flag bits");
set_field_flags(field_type,
((is_volatile ? 1 : 0) << is_volatile_shift) | <--- 在这里会设置volatile 的值
((is_final ? 1 : 0) << is_final_shift),
field_index);
set_bytecode_1(get_code);
set_bytecode_2(put_code);
NOT_PRODUCT(verify(tty));
}

然后用gdb调试一下:

(gdb) bt
#0 b ::set_field (this=0x7fffb4151b50, get_code=Bytecodes::_nop, put_code=Bytecodes::_nop, field_holder=0x800042fa0, field_index=2, field_offset=120, field_type=atos,
is_final=true, is_volatile=false) at /home/dai/jdk/src/hotspot/share/oops/cpCache.cpp:139
#1 0x00007ffff65d2561 in InterpreterRuntime::resolve_get_put (current=0x7ffff0028f70, bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:708
#2 0x00007ffff65d3e14 in InterpreterRuntime::resolve_from_cache (current=0x7ffff0028f70, bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:959
#3 0x00007fffe10203e3 in ?? ()
#4 0x00007ffff7bcc0a0 in TemplateInterpreter::_active_table () from /home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#5 0x00007fffe1020362 in ?? ()
#6 0x0000000000000000 in ?? ()

我们看看ConstantPoolCacheEntry是从哪里取的

// Helper class to access current interpreter state
class LastFrameAccessor : public StackObj {
frame _last_frame;
public:
...
ConstantPoolCacheEntry* cache_entry_at(int i) const
{ return method()->constants()->cache()->entry_at(i); }
ConstantPoolCacheEntry* cache_entry() const { return cache_entry_at(Bytes::get_native_u2(bcp() + 1)); }
}
(gdb) p m->print()
{method}
- this oop: 0x00007fffd14108e8
- method holder: 'java/lang/String'
- constants: 0x00007fffd14013b8 constant pool [1396]/operands[28] {0x00007fffd14013b8} for 'java/lang/String' cache=0x00007fffd1544158
- access: 0x8 static
- name: '<clinit>'
- signature: '()V'
- max stack: 3
- max locals: 0
- size of params: 0
- method size: 13
- vtable index: -2
- i2i entry: 0x00007fffe100dbe0
- adapters: AHE@0x00007ffff009b550: 0x i2c: 0x00007fffe1115060 c2i: 0x00007fffe111510d c2iUV: 0x00007fffe11150e0 c2iNCI: 0x00007fffe111514a
- compiled entry 0x00007fffe111510d
- code size: 22
- code start: 0x00007fffd14108c0
- code end (excl): 0x00007fffd14108d6
- checked ex length: 0
- linenumber start: 0x00007fffd14108d6
- localvar length: 0
$7 = void
(gdb) bt
#0 frame::interpreter_frame_method (this=0x7ffff5aa43f8) at /home/ubuntu/daixiao/jdk/src/hotspot/share/runtime/frame.cpp:332
#1 0x00007ffff672253c in LastFrameAccessor::method (this=0x7ffff5aa43f0) at /home/ubuntu/daixiao/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:90
#2 0x00007ffff671d2fb in InterpreterRuntime::resolve_get_put (current=0x7ffff0028940, bytecode=Bytecodes::_putstatic) at /home/ubuntu/daixiao/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:650
#3 0x00007ffff671ec81 in InterpreterRuntime::resolve_from_cache (current=0x7ffff0028940, bytecode=Bytecodes::_putstatic) at /home/ubuntu/daixiao/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:948
#4 0x00007fffe10203a3 in ?? ()
#5 0x00007ffff7d4b280 in TemplateInterpreter::_active_table () from /home/ubuntu/daixiao/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#6 0x00007fffe1020322 in ?? ()
#7 0x0000000000000001 in ?? ()
#8 0x00007ffff5aa44d8 in ?? ()
#9 0x00007fffd14108c1 in ?? ()
#10 0x00007ffff5aa4528 in ?? ()
#11 0x00007fffd1544158 in ?? ()
#12 0x0000000000000000 in ?? ()
(gdb)

解析cache

InterpreterRuntime::resolve_from_cache

LinkResolver::resolve_field_access

相关类在

//   src/hotspot/share/oops/cpCache.cpp
// 头文件 在 src/hotspot/share/oops/cpCache.hpp
ConstantPoolCacheEntry

问了一下chatgpt

在HotSpot JVM中,ConstantPoolCacheEntry对象是在解析类文件时动态创建的。具体来说,在解析类文件时,JVM会为类文件中的每个常量创建一个对应的ConstantPoolCacheEntry对象,并将其添加到类的常量池缓存中。

ConstantPoolCacheEntry对象的实例化主要是在ClassFileParser类的parse_constant_pool_entry()函数中完成的。在解析常量池时,parse_constant_pool_entry()函数会根据常量的类型和值动态创建一个对应的ConstantPoolCacheEntry对象,并将其添加到类的常量池缓存中。

需要注意的是,在解析过程中,JVM可能会使用占位符对象来表示无效的或未知的常量,而不是创建ConstantPoolCacheEntry对象。在这种情况下,JVM会将占位符对象添加到常量池缓存中,以便在解析和执行时可以快速跳过无效的或未知的常量。

总之,在HotSpot JVM中,ConstantPoolCacheEntry对象的实例化是在解析类文件时完成的,而具体实现是在ClassFileParser类的parse_constant_pool_entry()函数中完成的。

查看堆栈

(gdb) p stream->_source
$4 = 0x7ffff00bdd40 "/home/ubuntu/daixiao/jdk/build/linux-x86_64-server-slowdebug/jdk/modules/java.base"
(gdb) bt
#0 ClassFileParser::parse_constant_pool_entries (this=0x7ffff0028940, stream=0x0, cp=0x7ffff5fadff1 <Thread::as_Java_thread()+39>, length=32767, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classFileParser.cpp:150
#1 0x00007ffff6378b5e in ClassFileParser::parse_constant_pool (this=0x7ffff5aa27e0, stream=0x7ffff00298e0, cp=0x7fffd169b1c8, length=31, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classFileParser.cpp:424
#2 0x00007ffff638aacc in ClassFileParser::parse_stream (this=0x7ffff5aa27e0, stream=0x7ffff00298e0, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classFileParser.cpp:5720
#3 0x00007ffff638a2b1 in ClassFileParser::ClassFileParser (this=0x7ffff5aa27e0, stream=0x7ffff00298e0, name=0x7fffb005b0a0, loader_data=0x7ffff0091890, cl_info=0x7ffff5aa2a10, pub_level=ClassFileParser::BROADCAST, __the_thread__=0x7ffff0028940)
at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classFileParser.cpp:5590
#4 0x00007ffff69d5f1f in KlassFactory::create_from_stream (stream=0x7ffff00298e0, name=0x7fffb005b0a0, loader_data=0x7ffff0091890, cl_info=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/klassFactory.cpp:199
#5 0x00007ffff639a9e5 in ClassLoader::load_class (name=0x7fffb005b0a0, search_append_only=false, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classLoader.cpp:1222
#6 0x00007ffff6e97300 in SystemDictionary::load_instance_class_impl (class_name=0x7fffb005b0a0, class_loader=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:1290
#7 0x00007ffff6e976d1 in SystemDictionary::load_instance_class (name_hash=1923324215, name=0x7fffb005b0a0, class_loader=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:1356
#8 0x00007ffff6e95874 in SystemDictionary::resolve_instance_class_or_null (name=0x7fffb005b0a0, class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:724
#9 0x00007ffff6e94481 in SystemDictionary::resolve_instance_class_or_null_helper (class_name=0x7fffb005b0a0, class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:295
#10 0x00007ffff6e94330 in SystemDictionary::resolve_or_null (class_name=0x7fffb005b0a0, class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:278
#11 0x00007ffff6e94273 in SystemDictionary::resolve_or_fail (class_name=0x7fffb005b0a0, class_loader=..., protection_domain=..., throw_error=true, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:264
#12 0x00007ffff64314d1 in ConstantPool::klass_at_impl (this_cp=..., which=506, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/oops/constantPool.cpp:512
#13 0x00007ffff62aca88 in ConstantPool::klass_at (this=0x7fffd1692c38, which=506, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/oops/constantPool.hpp:420
#14 0x00007ffff671af3a in InterpreterRuntime::_new (current=0x7ffff0028940, pool=0x7fffd1692c38, index=506) at /home/ubuntu/daixiao/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:219
#15 0x00007fffe1023b92 in ?? ()
#16 0x00007fffe1023b06 in ?? ()
#17 0x00007ffff5aa3368 in ?? ()
#18 0x00007fffd1699fe1 in ?? ()
#19 0x00007ffff5aa33b8 in ?? ()
#20 0x00007fffd169a0a0 in ?? ()
#21 0x0000000000000000 in ?? ()

设置断点

(gdb) commands
Type commands for breakpoint(s) 5, one per line.
End with a line saying just "end".
>p name->print()
>c
>end

打印

Thread 2 "java" hit Breakpoint 5, KlassFactory::create_from_stream (stream=0x7ffff5aa2f20, name=0x7ffff044c4a0, loader_data=0x7ffff03ddf90, cl_info=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/klassFactory.cpp:172
172 assert(loader_data != NULL, "invariant");
Symbol: 'com/HelloWorld' count 2$1035 = void

类加载

$801 = 0x7ffff59fd0b0 "file:/home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/bin/"
(gdb) bt
#0 KlassFactory::create_from_stream (stream=0x7ffff59fce00, name=0x7ffff057a9d0, loader_data=0x7ffff05185c0, cl_info=..., __the_thread__=0x7ffff0028f50)
at /home/dai/jdk/src/hotspot/share/classfile/klassFactory.cpp:179
#1 0x00007ffff6d4248e in SystemDictionary::resolve_class_from_stream (st=0x7ffff59fce00, class_name=0x7ffff057a9d0, class_loader=..., cl_info=..., __the_thread__=0x7ffff0028f50)
at /home/dai/jdk/src/hotspot/share/classfile/systemDictionary.cpp:914
#2 0x00007ffff6d42708 in SystemDictionary::resolve_from_stream (st=0x7ffff59fce00, class_name=0x7ffff057a9d0, class_loader=..., cl_info=..., __the_thread__=0x7ffff0028f50)
at /home/dai/jdk/src/hotspot/share/classfile/systemDictionary.cpp:952
#3 0x00007ffff66e1b51 in jvm_define_class_common (name=0x7ffff59fd030 "Hello", loader=0x7ffff59fd5a0, buf=0x7ffff04a4b90 "\312\376\272\276", len=409, pd=0x7ffff59fd578,
source=0x7ffff59fd0b0 "file:/home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/bin/", __the_thread__=0x7ffff0028f50) at /home/dai/jdk/src/hotspot/share/prims/jvm.cpp:883
#4 0x00007ffff66e2832 in JVM_DefineClassWithSource (env=0x7ffff0029230, name=0x7ffff59fd030 "Hello", loader=0x7ffff59fd5a0, buf=0x7ffff04a4b90 "\312\376\272\276", len=409, pd=0x7ffff59fd578,
source=0x7ffff59fd0b0 "file:/home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/bin/") at /home/dai/jdk/src/hotspot/share/prims/jvm.cpp:1047
#5 0x00007ffff58db1f5 in Java_java_lang_ClassLoader_defineClass1 (env=0x7ffff0029230, cls=0x7ffff59fd560, loader=0x7ffff59fd5a0, name=0x7ffff59fd598, data=0x7ffff59fd590, offset=0, length=409,
pd=0x7ffff59fd578, source=0x7ffff59fd570) at /home/dai/jdk/src/java.base/share/native/libjava/ClassLoader.c:132
#6 0x00007fffe100f6cb in ?? ()
#7 0x0000000000000199 in ?? ()
#8 0x00007ffff59fd578 in ?? ()
#9 0x00007ffff59fd570 in ?? ()
#10 0x0000555555581230 in ?? ()
#11 0x00007ffff0028f50 in ?? ()
#12 0x00007fffb445ba08 in ?? ()
#13 0x00007fffe100f199 in ?? ()
#14 0x00007ffff59fd508 in ?? ()
#15 0x00007fffb4025170 in ?? ()
#16 0x00007ffff59fd5a0 in ?? ()
#17 0x00007fffb4147588 in ?? ()
#18 0x0000000000000000 in ?? ()

fileinfo 如何初始化

ConstantPoolCacheEntry 是由fieldDescriptor 的字段传进去的 , 那么我们看看fieldDescriptor是怎么初始化的

  fieldDescriptor info;
...
void InterpreterRuntime::resolve_get_put(JavaThread* current, Bytecodes::Code bytecode) {
...

LinkResolver::resolve_field_access(info, pool, last_frame.get_index_u2_cpcache(bytecode),
m, bytecode, CHECK);
...


}

我们最后找到初始化的地方

bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
Symbol* f_name = fs.name();
Symbol* f_sig = fs.signature();
if (f_name == name && f_sig == sig) {
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.index()); // 初始化的地方
return true;
}
}
return false;
}

初始化就在这里: fieldDescriptor::reinitialize

void fieldDescriptor::reinitialize(InstanceKlass* ik, int index) {
...
FieldInfo* f = ik->field(index);
_access_flags = accessFlags_from(f->access_flags());
guarantee(f->name_index() != 0 && f->signature_index() != 0, "bad constant pool index for fieldDescriptor");
_index = index;
...
}

jvm 堆栈结构

inline frame ContinuationEntry::to_frame() const {
static CodeBlob* cb = CodeCache::find_blob_fast(entry_pc());
assert(cb != nullptr, "");
assert(cb->as_compiled_method()->method()->is_continuation_enter_intrinsic(), "");
return frame(entry_sp(), entry_sp(), entry_fp(), entry_pc(), cb);
}
(gdb) bt
#0 frame::frame (this=0x7ffff59fe348) at /home/dai/jdk/src/hotspot/cpu/x86/frame_x86.inline.hpp:37
#1 0x00007ffff65d76f1 in LastFrameAccessor::LastFrameAccessor (this=0x7ffff59fe340, current=0x7ffff0028f70)
at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:84
#2 0x00007ffff65d212e in InterpreterRuntime::resolve_get_put (current=0x7ffff0028f70,
bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:652
#3 0x00007ffff65d3e14 in InterpreterRuntime::resolve_from_cache (current=0x7ffff0028f70,
bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:959
#4 0x00007fffe10203e3 in ?? ()
#5 0x00007ffff7bca0a0 in TemplateInterpreter::_active_table ()
from /home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#6 0x00007fffe1020362 in ?? ()
#7 0x0000000000000001 in ?? ()
#8 0x00007ffff59fe438 in ?? ()
#9 0x00007fffb4010821 in ?? ()
#10 0x00007ffff59fe488 in ?? ()
#11 0x00007fffb4149b38 in ?? ()
#12 0x0000000000000000 in ?? ()

后面我们能看到初始化是在这里:

frame JavaThread::pd_last_frame() {
assert(has_last_Java_frame(), "must have last_Java_sp() when suspended");
vmassert(_anchor.last_Java_pc() != NULL, "not walkable");
return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp(), _anchor.last_Java_pc());
}

frame 的初始化

(gdb) bt
#0 JavaThread::pd_last_frame (this=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/os_cpu/linux_x86/thread_linux_x86.cpp:30
#1 0x00007ffff612888b in JavaThread::last_frame (this=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/share/runtime/thread.hpp:1407
#2 0x00007ffff65d7757 in LastFrameAccessor::LastFrameAccessor (this=0x7ffff59fe340, current=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:86
#3 0x00007ffff65d212e in InterpreterRuntime::resolve_get_put (current=0x7ffff0028f70, bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:652
#4 0x00007ffff65d3e14 in InterpreterRuntime::resolve_from_cache (current=0x7ffff0028f70, bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:959
#5 0x00007fffe10203e3 in ?? ()
#6 0x00007ffff7bca0a0 in TemplateInterpreter::_active_table () from /home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#7 0x00007fffe1020362 in ?? ()
#8 0x0000000000000001 in ?? ()
#9 0x00007ffff59fe438 in ?? ()
#10 0x00007fffb4010821 in ?? ()
#11 0x00007ffff59fe488 in ?? ()
#12 0x00007fffb4149b38 in ?? ()
#13 0x0000000000000000 in ?? ()
(gdb) list
35 // For Forte Analyzer AsyncGetCallTrace profiling support - thread is
36 // currently interrupted by SIGPROF
37 bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr,
38 void* ucontext, bool isInJava) {
39
40 assert(Thread::current() == this, "caller must be current thread");
41 return pd_get_top_frame(fr_addr, ucontext, isInJava);
42 }
43
44 bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava) {
(gdb) info registers
rax 0x7ffff59fe220 140737314284064
rbx 0x7ffff59fe340 140737314284352
rcx 0x7ffff00153f0 140737220006896
rdx 0x7ffff0028f70 140737220087664
rsi 0x7ffff0028f70 140737220087664
rdi 0x7ffff59fe220 140737314284064
rbp 0x7ffff59fe1f0 0x7ffff59fe1f0
rsp 0x7ffff59fe1d0 0x7ffff59fe1d0
r8 0x8 8
r9 0x0 0
r10 0x7ffff7bca0a0 140737349722272
r11 0x7ffff0000090 140737219920016
r12 0x0 0
r13 0x7fffb4010821 140736213354529
r14 0x7ffff59fe488 140737314284680
r15 0x7ffff0028f70 140737220087664
rip 0x7ffff6da2ea3 0x7ffff6da2ea3 <JavaThread::pd_last_frame()+23>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0

然后我们看到在

  JavaFrameAnchor _anchor;    

class JavaFrameAnchor {
...

private:
...
intptr_t* volatile _last_Java_sp; //stack_pointer
...
}

JavaFrameAnchor 初始化

JavaFrameAnchor::clear (this=0x7ffff0029230) at /home/dai/jdk/src/hotspot/cpu/x86/javaFrameAnchor_x86.hpp:44
44 _last_Java_fp = NULL;
(gdb) bt
#0 JavaFrameAnchor::clear (this=0x7ffff0029230) at /home/dai/jdk/src/hotspot/cpu/x86/javaFrameAnchor_x86.hpp:44
#1 0x00007ffff65df910 in JavaFrameAnchor::JavaFrameAnchor (this=0x7ffff0029230) at /home/dai/jdk/src/hotspot/share/runtime/javaFrameAnchor.hpp:88
#2 0x00007ffff6d8730c in JavaThread::JavaThread (this=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:1076
#3 0x00007ffff6d8d0ee in Threads::create_vm (args=0x7ffff59fed50, canTryAgain=0x7ffff59fec5b) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:2817
#4 0x00007ffff66b243b in JNI_CreateJavaVM_inner (vm=0x7ffff59feda8, penv=0x7ffff59fedb0, args=0x7ffff59fed50) at /home/dai/jdk/src/hotspot/share/prims/jni.cpp:3613
#5 0x00007ffff66b2787 in JNI_CreateJavaVM (vm=0x7ffff59feda8, penv=0x7ffff59fedb0, args=0x7ffff59fed50) at /home/dai/jdk/src/hotspot/share/prims/jni.cpp:3701
#6 0x00007ffff7faca6a in InitializeJVM (pvm=0x7ffff59feda8, penv=0x7ffff59fedb0, ifn=0x7ffff59fee00) at /home/dai/jdk/src/java.base/share/native/libjli/java.c:1459
#7 0x00007ffff7fa95ec in JavaMain (_args=0x7fffffffa9a0) at /home/dai/jdk/src/java.base/share/native/libjli/java.c:411
#8 0x00007ffff7fb05ec in ThreadJavaMain (args=0x7fffffffa9a0) at /home/dai/jdk/src/java.base/unix/native/libjli/java_md.c:651
#9 0x00007ffff7c94b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#10 0x00007ffff7d26a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

在这里会设置last_java_sp

void MacroAssembler::call_VM_base(Register oop_result,
Register java_thread,
Register last_java_sp,
address entry_point,
int number_of_arguments,
bool check_exceptions) {
// determine java_thread register
if (!java_thread->is_valid()) {
#ifdef _LP64
java_thread = r15_thread;
#else
java_thread = rdi;
get_thread(java_thread);
#endif // LP64
}
// determine last_java_sp register
if (!last_java_sp->is_valid()) {
last_java_sp = rsp;
}
// debugging support
assert(number_of_arguments >= 0 , "cannot have negative number of arguments");
LP64_ONLY(assert(java_thread == r15_thread, "unexpected register"));
#ifdef ASSERT
// TraceBytecodes does not use r12 but saves it over the call, so don't verify
// r12 is the heapbase.
LP64_ONLY(if (UseCompressedOops && !TraceBytecodes) verify_heapbase("call_VM_base: heap base corrupted?");)
#endif // ASSERT

assert(java_thread != oop_result , "cannot use the same register for java_thread & oop_result");
assert(java_thread != last_java_sp, "cannot use the same register for java_thread & last_java_sp");

// push java thread (becomes first argument of C function)

NOT_LP64(push(java_thread); number_of_arguments++);
LP64_ONLY(mov(c_rarg0, r15_thread));

// set last Java frame before call
assert(last_java_sp != rbp, "can't use ebp/rbp");

// Only interpreter should have to set fp
set_last_Java_frame(java_thread, last_java_sp, rbp, NULL);

// do the call, remove parameters
MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments);

// restore the thread (cannot use the pushed argument since arguments
// may be overwritten by C code generated by an optimizing compiler);
// however can use the register value directly if it is callee saved.
if (LP64_ONLY(true ||) java_thread == rdi || java_thread == rsi) {
// rdi & rsi (also r15) are callee saved -> nothing to do
#ifdef ASSERT
guarantee(java_thread != rax, "change this code");
push(rax);
{ Label L;
get_thread(rax);
cmpptr(java_thread, rax);
jcc(Assembler::equal, L);
STOP("MacroAssembler::call_VM_base: rdi not callee saved?");
bind(L);
}
pop(rax);
#endif
} else {
get_thread(java_thread);
}
// reset last Java frame
// Only interpreter should have to clear fp
reset_last_Java_frame(java_thread, true);

// C++ interp handles this in the interpreter
check_and_handle_popframe(java_thread);
check_and_handle_earlyret(java_thread);

if (check_exceptions) {
// check for pending exceptions (java_thread is set upon return)
cmpptr(Address(java_thread, Thread::pending_exception_offset()), (int32_t) NULL_WORD);
#ifndef _LP64
jump_cc(Assembler::notEqual,
RuntimeAddress(StubRoutines::forward_exception_entry()));
#else
// This used to conditionally jump to forward_exception however it is
// possible if we relocate that the branch will not reach. So we must jump
// around so we can always reach

Label ok;
jcc(Assembler::equal, ok);
jump(RuntimeAddress(StubRoutines::forward_exception_entry()));
bind(ok);
#endif // LP64
}

// get oop result if there is one and reset the value in the thread
if (oop_result->is_valid()) {
get_vm_result(oop_result, java_thread);
}
}

volatile 标志位

///home/dai/jdk/src/hotspot/share/utilities/accessFlags.hpp
bool is_volatile () const { return (_flags & JVM_ACC_VOLATILE ) != 0; }

相关阅读