java字节码分析(jclasslib)-synchronized原理

  • Post author:
  • Post category:java




说明

原理:synchronized代码块同步,即一段指令系列的同步,主要是通过monitorenter、monitorexit来实现的,为了确保执行monitorenter后必须执行monitorexit,编译器会自动产生一个异常处理器,目的就是用来执行monitorexit;

下面将对下面代码进行字节码分析

package com.ydfind.test;

public class Monitor {
    private void test() {
        synchronized (this) {
            int a = 1;
        }
    }
    synchronized
    public void test1() {
    }
}



synchronized代码块分析



字节码

字节码及注释如下:

在这里插入图片描述

 0 aload_0       // 将this压入操作数栈
 1 dup           // 将栈顶this复制并压入栈
 2 astore_1      // 将栈顶this放入变量1。变量1是准备做为monitorexit的参数
 3 monitorenter  // 尝试持有栈顶this的管程(this出栈后,栈就空了)
 // 下面2行实现int a = 1功能,1保存在变量2里面
 4 iconst_1
 5 istore_2
 6 aload_1       // monitorexit this
 7 monitorexit
 8 goto 16 (+8)
 11 astore_3     // 发生异常,将异常对象放入变量3----为什么不放入变量2呢?
 12 aload_1      // monitorexit this
 13 monitorexit
 14 aload_3      // monitorexit后再抛出异常
 15 athrow
 16 return



杂项

上面字节码astore_n中n最大为3,及局部变量表深度4;操作数栈开始时两个this,最大深度为2。与杂项里面的记录一致:

在这里插入图片描述



异常表

起始PC:异常处理器 监控代码的起始位置

结束PC:异常处理器 监控代码的结束位置(并不包括结束行,即[startPC,endPC))

跳转PC:处理异常的起始位置,比如catch代码块

捕获类型:异常处理器所捕获的异常类型

关于异常表如下所示。若是同步代码块出现异常,会跳到11行,正常情况下,释放管程后,再抛出异常;但若11-13行发生异常,会不断跳到11行,即不断尝试释放管程,这是确保异常发生时能确保管程能释放的保护机制。

在这里插入图片描述



行号表

起始PC: 字节码的起始位置;

行号:代码行号;

在这里插入图片描述



操作数栈

起始位置:变量作用域的起始位置

长度:变量作用域的长度。例子中this在0-16总共17行中生效,即函数内都有效;

序号:数组的下标

名字:变量的描述(变量类型+引用类型 or 基本类型)

在这里插入图片描述



synchronized方法分析

在这里插入图片描述

方法级的同步是隐式的,在方法的调用和返回操作之中实现。方法调用时,调用指令会检查方法的ACC_SYNCHRONIZED标志是否被设置了,若是被设置了,则当前线程需要持有管程才能执行方法,执行完成则会释放管程。在方法执行过程中,执行线程持有管程,故其它线程会无法获得管程而无法执行程序(

竞争失败的线程会被放入entrylist

);若是发生异常,且函数内部无法处理,那么管程会在 异常向外方法抛出时 自动释放。

上面可以看出,方法的访问标识里面有synchronized,则表示是同步方法。

运行时,jvm可以通过检查方法常量池的方法表结构的ACC_SYNCHRONIZED访问标志 得知该方法 是否被声明为同步方法。既然是方法常量池,则是在方法区。



版权声明:本文为sndayYU原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。