目录
2、InterpreterGenerator::lock_method
二、jni_MonitorEnter / jni_MonitorExit
三、Unsafe_MonitorEnter / Unsafe_MonitorExit / Unsafe_TryMonitorEnter
本篇博客继续上一篇《Hotspot synchronized与volatile关键字实现(一) 源码解析》讲解 synchronized与volatile关键字的底层实现。
一、synchronized底层实现
上一篇中分析了修饰代码块时在解释执行和编译执行下的synchronized底层实现,我们继续探讨在修饰方法时的synchronized底层实现逻辑。
1、修饰代码块字节码分析
测试用例如下:
public class AddTest {
private int a;
private static int b;
synchronized int add(){
a++;
return a;
}
synchronized static int add2(){
b++;
return b;
}
synchronized native int addn();
synchronized native static int add2n();
}
将上述java代码编译后,执行javap -v命令可查看具体的字节码内容,如下:
synchronized int add();
descriptor: ()I
flags: ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #2 // Field a:I
5: iconst_1
6: iadd
7: putfield #2 // Field a:I
10: aload_0
11: getfield #2 // Field a:I
14: ireturn
LineNumberTable:
line 9: 0
line 10: 10
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this LsynchronizedTest/AddTest;
static synchronized int add2();
descriptor: ()I
flags: ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=0, args_size=0
0: getstatic #3 // Field b:I
3: iconst_1
4: iadd
5: putstatic #3 // Field b:I
8: getstatic #3 // Field b:I
11: ireturn
LineNumberTable:
line 14: 0
line 15: 8
synchronized native int addn();
descriptor: ()I
flags: ACC_SYNCHRONIZED, ACC_NATIVE
static synchronized native int add2n();
descriptor: ()I
flags: ACC_STATIC, ACC_SYNCHRONIZED, ACC_NATIVE
其中跟业务逻辑相关的指令和之前的修饰代码块时是一样的,少了monitorenter和monitorexit指令及其异常处理指令,但是方法的flags多了,ACC_STATIC表示静态方法,ACC_SYNCHRONIZED表示这是一个synchronized修饰的方法,ACC_NATIVE表示这是这个本地方法,JVM在解释执行或者编译执行时会根据flags做不同的处理。
2、InterpreterGenerator::lock_method
普通方法解释执行和本地方法执行的Stub的生成可以参考《Hotspot 字节码执行与栈顶缓存实现 源码解析》中的AbstractInterpreterGenerator::generate_method_entry方法的讲解,如下图:

其中zerolocals表示普通的实例方法,zerolocals_synchronized表示被 synchronized关键字修饰的实例方法,native表示普通的本地方法,native_synchronized表示被 synchronized关键字修饰的本地方法。实例方法通过generate_normal_entry方法生成调用stub,本地方法通过generate_native_entry生成调用stub,这两个方法都有一个bool参数表示是否需要加锁,对generate_normal_entry而言,其入参就是截图中的synchronized变量。这两方法执行加锁的逻辑是一样,如下:

ASSERT代码块用于检测该方法的flags不包含ACC_SYNCHRONIZED,即不是synchronized关键字修饰的方法,lock_method方法的实现如下:
void InterpreterGenerator::lock_method(void) {
// synchronize method
const Address access_flags(rbx, Method::access_flags_offset());
const Address monitor_block_top(
rbp,
frame::interpreter_frame_monitor_block_top_offset * wordSize);
const int entry_size = frame::interpreter_frame_monitor_size() * wordSize;
// get synchronization object
{
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
Label done;
//将方法的access_flags拷贝到rax中
__ movl(rax, access_flags);
//校验这个方法是否是静态方法
__ testl(rax, JVM_ACC_STATIC);
//将栈顶的执行方法调用的实例拷贝到rax中
__ movptr(rax, Address(r14, Interpreter::local_offset_in_bytes(0)));
//如果不是静态方法,则跳转到done
__ jcc(Assembler::zero, done);
//如果是静态方法,获取该Method对应的真实Klass,即pool_holder属性
__ movptr(rax, Address(rbx, Method::const_offset()));
__ movptr(rax, Address(rax, ConstMethod::constants_offset()));
__ movptr(rax, Address(rax,
ConstantPool::pool_holder_offset_in_bytes()));
//将Klass的java_mirror属性复制到到rax中,即某个类对应的class实例
__ movptr(rax, Address(rax, mirror_offset));
__ bind(done);
}
//将rsp往下,即低地址端移动entry_size,即一个BasicObjectLock的大小
__ subptr(rsp, entry_size); // add space for a monitor entry
//将rsp地址写入栈帧中monitor_block_top地址
__ movptr(monitor_block_top, rsp); // set new monitor block top
//将rax即跟锁关联的对象保存到BasicObjectLock的obj属性
__ movptr(Address(rsp, BasicObjectLock::obj_offset_in_bytes()), rax);
//将rsp地址拷贝到c_rarg1,即BasicObjectLock实例的地址
__ movptr(c_rarg1, rsp); // object address
//调用lock_object加锁
__ lock_object(c_rarg1);
}
实现加锁的lock_object方法跟 synchronized修饰代码块是调用方法时一样的,都是InterpreterMacroAssembler::lock_object方法,参考其调用链如下:

其中TemplateTable::monitorenter就是monitorenter字节码指令的实现。
3、执行本地方法synchronized解锁
Java代码中执行本地方法时比较特殊,具体逻辑可以参考《Hotspot 本地方法绑定与执行 源码解析》,本地方法执行完成返回到上一次Java代码的调用的逻辑也是在generate_native_entry中,在返回前就需要执行synchronized解锁,其实现如下:
// do unlocking if necessary
{
Label L;
//获取方法的access_flags
__ movl(t, Address(method, Method::access_flags_offset()));
__ testl(t, JVM_ACC_SYNCHRONIZED);
//如果不是synchronized方法跳转到L
__ jcc(Assembler::zero, L);
//如果是synchronized方法执行解锁
{
Label unlock;
// BasicObjectLock will be first in list, since this is a
// synchronized method. However, need to check that the object
// has not been unlocked by an explicit monitorexit bytecode.
//取出放在Java栈帧头部的BasicObjectLock
const Address monitor(rbp,
(intptr_t)(frame::interpreter_frame_initial_sp_offset *
wordSize - sizeof(BasicObjectLock)));
//将monitor的地址即关联的BasicObjectLock的地址放入c_rarg1中
__ lea(c_rarg1, monitor); // address of first monitor
//将BasicObjectLock的obj属性赋值到t中
__ movptr(t, Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()));
//判断obj属性是否为空
__ testptr(t, t);
//如果不为空,则跳转到unlock
__ jcc(Assembler::notZero, unlock);
//如果为空,说明未上锁,则抛出异常
__ MacroAssembler::call_VM(noreg,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::throw_illegal_monitor_state_exception));
__ should_not_reach_here();
__ bind(unlock);
//解锁
__ unlock_object(c_rarg1);
}
__ bind(L);
}
上述unlock_object方法跟修饰代码块解锁时调用InterpreterMacroAssembler::unlock_object方法,其调用链如下:

其中TemplateTable::monitorexit方法就是monitorexit指令的实现,remove_activation方法用于实现return指令,抛出异常的stub等。
4、解释执行普通Java方法synchronized解锁
普通Java方法执行完成后需要通过return系列指令返回到方法的调用栈帧,synchronized解锁就是在return系列指令中完成的。return系列指令一共有7个,其底层实现都是同一个方法,参考TemplateTable::initialize方法的定义,如下图:

ireturn表示返回一个int值,lreturn表示返回一个long值,freturn表示返回一个float值, dreturn表示返回一个double值,areturn表示返回一个对象引用,return表示返回void,除上述6个外OpenJDK还增加了一个_return_register_finalizer,跟return一样都是返回void,不同的是如果目标类实现了finalize方法则会注册对应的Finalizer。_return方法的实现如下:
void TemplateTable::_return(TosState state) {
transition(state, state);
assert(_desc->calls_vm(),
"inconsistent calls_vm information"); // call in remove_activation
//如果当前字节码是_return_register_finalizer,这个是OpenJDK特有的
if (_desc->bytecode() == Bytecodes::_return_register_finalizer) {
assert(state == vtos, "only valid state");
__ movptr(c_rarg1, aaddress(0));
//c_rarg1保存了方法调用的oop,获取其klass
__ load_klass(rdi, c_rarg1);
//获取类的access_flags,判断其是否实现了finalizer方法
__ movl(rdi, Address(rdi, Klass::access_flags_offset()));
__ testl(rdi, JVM_ACC_HAS_FINALIZER);
Label skip_register_finalizer;
//如果未实现,则跳转到skip_register_finalizer
__ jcc(Assembler::zero, skip_register_finalizer);
//如果实现了,则执行register_finalizer方法
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::register_finalizer), c_rarg1);
__ bind(skip_register_finalizer);
}
if (state == itos) {
//如果返回值是int,则执行narrow方法,会根据方法的返回值类型做特殊处理,因为在JVM中char,byte,boolean三种
//都是作为int处理的,如果返回值类型是上述的三种之一则将返回值由4个字节调整成对

最低0.47元/天 解锁文章
569





