范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

深入浅出JVM(十)之字节码指令

  本篇文章主要围绕字节码的指令,深入浅出的解析各种类型字节码指令,如:加载存储、算术、类型转换、对象创建与访问、方法调用与返回、控制转义、异常处理、同步等
  由于字节码指令种类太多,本文作为上篇概述加载存储、算术、类型转换的字节码指令
  使用idea中的插件jclasslib查看编译后的字节码指令字节码指令集
  大部分指令先以i(int)、l(long)、f(float)、d(double)、a(引用)开头
  其中byte、char、short、boolean在hotspot中都是转成int去执行(使用int类型的字节码指令)
  字节码指令大致分为:加载与存储指令算术指令类型转换指令对象创建与访问指令方法调用与返回指令操作数栈管理指令控制转义指令异常处理指令同步控制指令
  在hotspot中每个方法对应的一组字节码指令
  这组字节码指令在该方法所对应的栈帧中的局部变量表和操作数栈上进行操作
  字节码指令包含字节码操作指令 和 操作数 (操作数可能是在局部变量表上也可能在常量池中还可能就是常数)加载与存储指令
  加载
  加载指令就是把操作数加载到操作数栈中(可以从局部变量表,常量池中加载到操作数栈)局部变量表加载指令i/l/f/d/aload 后面跟的操作数就是要去局部变量表的哪个槽取值iload_0: 去局部变量表0号槽取出int类型值常量加载指令可以根据加载的常量范围分为三种(从小到大) const < push < ldc
  存储
  存储指令就是将操作数栈顶元素出栈后,存储到局部变量表的某个槽中存储指令i/l/f/d/astore 后面跟的操作数就是要存到局部变量表的哪个槽istore_1:出栈栈顶int类型的元素保存到局部变量表的1号槽
  注意: 编译时就知道了局部变量表应该有多少槽的位置 和 操作数栈的最大深度(为节省空间,局部变量槽还会复用)
  从常量池加载100存储到局部变量表1号槽,从常量池加载200存储到局部变量表2号槽(其中局部变量表0号槽存储this)算术指令
  算术指令将操作数栈中的俩个栈顶元素出栈作运算再将运算结果入栈
  使用的是后缀表达式(逆波兰表达式),比如 3 4 + => 3 + 4
  注意当除数是0时会抛出ArithmeticException异常浮点数转整数向0取整浮点数计算精度丢失Infinity 计算结果无穷大Nan 计算结果不确定计算值     public void test1() {          double d1 = 10 / 0.0;          //Infinity          System.out.println(d1);            double d2 = 0.0 / 0.0;          //NaN          System.out.println(d2);  ​          //向0取整模式:浮点数转整数          //5          System.out.println((int) 5.9);          //-5          System.out.println((int) -5.9);  ​  ​          //向最接近数舍入模式:浮点数运算          //0.060000000000000005          System.out.println(0.05+0.01);  ​          //抛出ArithmeticException: / by zero异常          System.out.println(1/0);      } 复制代码类型转换指令
  类型转换指令可以分为宽化类型转换和窄化类型转换(对应基本类型的非强制转换和强制转换)
  宽化类型转换
  小范围向大范围转换int -> long -> float -> doublei2l,i2f,i2dl2f,l2df2d
  byte、short、char 使用int类型的指令
  注意: long转换为float或double时可能发生精度丢失     public void test2(){          long l1 =  123412345L;          long l2 =  1234567891234567899L;  ​          float f1 = l1;          //结果: 1.23412344E8 => 123412344          //                l1 =  123412345L          System.out.println(f1);  ​          double d1 = l2;          //结果: 1.23456789123456794E18 => 1234567891234567940          //                          l2 =  1234567891234567899L          System.out.println(d1);      } 复制代码
  窄化类型转换
  大范围向小范围转换int->byte、char、short: i2b,i2c,i2slong->int: l2ifloat->long、int: f2l,f2idouble->float、long、int: d2f,d2l,d2i
  如果long,float,double要转换为byte,char,short可以先转为int再转为相对应类型
  窄化类型转换会发生精度丢失
  NaN和Infinity的特殊情况:     public void test3(){          double d1 = Double.NaN;          double d2 = Double.POSITIVE_INFINITY;  ​          int i1 = (int) d1;          int i2 = (int) d2;          //0          System.out.println(i1);          //true          System.out.println(i2==Integer.MAX_VALUE);  ​          long l1 = (long) d1;          long l2 = (long) d2;          //0          System.out.println(l1);          //true          System.out.println(l2==Long.MAX_VALUE);  ​          float f1 = (float) d1;          float f2 = (float) d2;          //NaN          System.out.println(f1);          //Infinity          System.out.println(f2);      } 复制代码
  NaN转为整型会变成0
  正无穷或负无穷转为整型会变成那个类型的最大值或最小值对象创建与访问指令
  对象创建与访问指令: 创建指令、字段访问指令、数组操作指令、类型检查指令创建指令
  new: 创建实例
  newarray: 创建一维基本类型数组
  anewarray: 创建一维引用类型数组
  multianewarray: 创建多维数组
  注意: 这里的创建可以理解为分配内存,当多维数组只分配了一维数组时使用的是anewarray
  字段访问指令
  getstatic: 对静态字段进行读操作
  putstatic: 对静态字段进行写操作
  getfield: 对实例字段进行读操作
  putfield: 对实例字段进行写操作
  读操作: 把要进行读操作的字段入栈
  写操作: 把要写操作的值出栈再写到对应的字段
  数组操作指令b/c/s/i/l/f/d/a aload : 表示将数组中某索引元素入栈 (读)需要的参数从栈顶依次向下: 索引位置、数组引用b/c/s/i/l/f/d/a astore: 表示将某值出栈并写入数组某索引元素 (写)需要的参数从栈顶依次向下: 要写入的值、索引位置、数组引用
  注意: b开头的指令对byte和boolean通用arraylength: 先将数组引用出栈再将获得的数组长度入栈
  类型检查指令
  instanceof: 判断某对象是否为某类的实例
  checkcast: 检查引用类型是否可以强制转换
  总结
  由于字节码指令种类多篇幅长,将会分为上、下篇来深入浅出解析字节码指令,本篇作为上篇深入浅出的解析字节码指令介绍、加载存储指令、算术指令、类型转换指令以及对象创建与访问指令
  字节码指令大部分以i、l、f、d、a开头,分别含义对应int、long、float、double、引用,其中byte、char、short、boolean会转换为int来执行
  字节码指令分为字节码操作指令和需要操作的数据,数据可能来源于局部变量表或常量池
  加载指令从局部变量表或者常量池中加载数据,存储指令将存储到对应局部变量表的槽中,实例方法的局部变量表的0号槽常用来存储this,如果方法中变量是局部存在的还可能会复用槽
  算术指令为各种类型和各种算术提供算术规则,在操作数栈中使用后缀表达式对操作数进行算术
  类型转换分为宽化与窄化,都可能存在精度损失
  对象创建与访问指令中包含创建对象,访问实例、静态字段,操作数组,类型检查等指令方法调用与返回指令方法调用指令
  非虚方法: 静态方法,私有方法,父类中的方法,被final修饰的方法,实例构造器
  与之对应不是非虚方法的就是虚方法了普通调用指令invokestatic: 调用静态方法invokespecial: 调用私有方法,父类中的方法,实例构造器方法,final方法invokeinterface: 调用接口方法invokevirtual: 调用虚方法使用invokestatic和invokespecial指令的一定是非虚方法使用invokeinterface指令一定是虚方法(因为接口方法需要具体的实现类去实现)使用invokevirtual指令可能是虚方法动态调用指令invokedynamic: 动态解析出需要调用的方法再执行jdk 7 出现invokedynamic,支持动态语言
  测试虚方法代码父类 public class Father {      public static void staticMethod(){          System.out.println("father static method");      }  ​      public final void finalMethod(){          System.out.println("father final method");      }  ​      public Father() {          System.out.println("father init method");      }  ​      public void overrideMethod(){          System.out.println("father override method");      }  } 复制代码接口 public interface TestInterfaceMethod {      void testInterfaceMethod();  } 复制代码子类 public class Son extends Father{  ​      public Son() {          //invokespecial 调用父类init 非虚方法          super();          //invokestatic 调用父类静态方法 非虚方法          staticMethod();          //invokespecial 调用子类私有方法 特殊的非虚方法          privateMethod();          //invokevirtual 调用子类的重写方法 虚方法          overrideMethod();          //invokespecial 调用父类方法 非虚方法          super.overrideMethod();          //invokespecial 调用父类final方法 非虚方法          super.finalMethod();          //invokedynamic 动态生成接口的实现类 动态调用          TestInterfaceMethod test = ()->{              System.out.println("testInterfaceMethod");          };          //invokeinterface 调用接口方法 虚方法          test.testInterfaceMethod();      }  ​      @Override      public void overrideMethod(){          System.out.println("son override method");      }  ​      private void privateMethod(){          System.out.println("son private method");      }  ​      public static void main(String[] args) {          new Son();      }  } 复制代码
  方法返回指令
  方法返回指令: 方法结束前,将栈顶元素(最后一个元素)出栈 ,返回给调用者
  根据方法的返回类型划分多种指令
  操作数栈管理指令
  通用型指令,不区分类型出栈pop/pop2出栈1个/2个栈顶元素入栈dup/dup2 复制栈顶1个/2个slot并重新入栈dup_x1 复制栈顶1个slot并插入到栈顶开始的第2个slot下dup_x2复制栈顶1个slot并插入到栈顶开始的第3个slot下dup2_x1复制栈顶2个slot并插入到栈顶开始的第3个slot下dup2_x2复制栈顶2个slot并插入到栈顶开始的第4个slot下插入到具体的slot计算: dup的系数 + _x的系数控制转义指令条件跳转指令
  通常先进行比较指令,再进行条件跳转指令
  比较指令比较结果-1,0,1再进行判断是否要跳转
  条件跳转指令: 出栈栈顶元素,判断它是否满足条件,若满足条件则跳转到指定位置
  注意: 这种跳转指令一般都"取反",比如代码中第一个条件语句是d>100,它第一个条件跳转指令就是ifle小于等于0,满足则跳转,不满足则按照顺序往下走比较条件跳转指令
  比较条件跳转指令 类似 比较指令和条件跳转指令 的结合体
  多条件分支跳转指令
  多条件分支跳转指令是为了switch-case提出的
  tableswitch用于case值连续的switch多条件分支跳转指令,效率好
  lookupswitch用于case值不连续的switch多条件分支跳转指令(虽然case值不连续,但最后会对case值进行排序)
  tableswitch
  lookupswitch
  对于String类型是先找到对应的哈希值再equals比较确定走哪个case的无条件跳转指令
  无条件跳转指令就是跳转到某个字节码指令处
  goto经常使用
  jsr,jsr_w,ret不怎么使用了
  异常处理指令
  throw抛出异常对应athrow: 清除该操作数栈上所有内容,将异常实例压入调用者操作数栈上
  使用try-catch/try-final/throws时会产生异常表
  异常表保存了异常处理信息 (起始、结束位置、字节码指令偏移地址、异常类在常量池中的索引等信息)
  athrow
  异常表
  异常还会被压入栈或者保存到异常表中同步控制指令
  synchronized作用于方法时,方法的访问标识会有ACC_SYNCHRONIZED表示该方法需要加锁
  synchronized作用于某个对象时,对应着**monitorentry加锁字节码指令和 monitorexit解锁字节码指令**
  Java中的synchronized默认是可重入锁当线程要访问需要加锁的对象时 (执行monitorentry)先查看对象头中加锁次数,如果为0说明未加锁,获取后,加锁次数自增如果不为0,再查看获取锁的线程是不是自己,如果是自己就可以访问,加锁次数自增如果不为0且获取锁线程不是自己,就阻塞
  当线程释放锁时 (执行monitorexit)会让加锁次数自减
  为什么会有2个monitorexit ?
  程序正常执行应该是一个monitorentry对应一个monitorexit的
  如果程序在加锁的代码中抛出了异常,没有释放锁,那不就会造成其他阻塞的线程永远也拿不到锁了吗
  所以在程序抛出异常时(跳转PC偏移量为15的指令)继续往下执行,抛出异常前要释放锁总结
  本篇文章作为字节码指令的下篇,深入浅出的解析方法调用与返回,操作数栈的入栈、出栈,控制转义,异常和同步相关字节码指令
  方法调用指令分为静态、私有、接口、虚、动态方法等,返回指令则主要是以i、l、f、d、a开头的return指令分别处理不同类型的返回值
  操作数栈中的出栈指令常用pop相关指令,入栈(复制栈顶元素并插入)常用dup相关指令
  控制转义指令中条件跳转指令是判断栈顶元素来进行跳转,比较条件跳转指令是通过两个栈顶元素比较来判断跳转,多条件分支跳转是满足switch,常在异常时进行goto无条件跳转
  异常处理指令用于抛出异常,清除操作数栈并将异常压入调用者操作数栈顶
  同步控制指令常使用monitorentry和monitoryexit,为了防止异常时死锁,抛异常前执行monitoryexit最后参考资料《深入理解Java虚拟机》
  作者:菜菜的后端私房菜
  链接:https://juejin.cn/post/7179445854070112316

小拉皮和大拉皮有什么区别?整形医生来详细阐述50岁女性因为丈夫讨厌她老去的容貌而出轨了,于是妻子决定改变自己,听朋友说做小拉皮不错,于是就去做了这种手术,之后她脸部皮肤变得更紧致,感觉自己又回到了二十岁。丈夫看到她年轻的容颜2023年电视动画重点项目申报开始,丝绸之路电视共同体高峰论坛成功举办整理自国家广播电视总局纪录中国综艺报订阅010860920622月11日2月17日政策1开展2023年度中国经典民间故事动漫创作工程(网络动画片)扶持项目征集活动的通知按照中办国办游戏出海收入下降第二增长曲线受阻,业内如何看2023年?当下国内游戏市场已经确定进入存量市场,寻找游戏出海的第二增长曲线是游戏企业迫在眉睫的大事,但2022年的数据显示,游戏出海收入规模也在下降。2月13日,中国游戏产业年会期间发布的2户外沉浸式剧本游戏现身江汉路,游客在百年街区探寻江城往事极目新闻记者康旭阳通讯员张凯最近,在武汉江汉路步行街,大型街区沉浸式互动游戏江城往事,吸引了大批游客打卡。该游戏以江汉路历史上的真实事件为蓝本,以整个江汉路为数字应用场景,融入街区海信电视可以买吗?这四款性价比高,75英寸仅2899元说到国产电视品牌,相信很多用户都知道海信,它已经有数十年的发展史了,更是在电视领域不断深耕,以往一听到海信电视,就觉得特别高级,但随着智能化的发展,电视市场中卷入了很多互联网品牌,送给长辈,还在纠结华为和vivo吗?这4款机型最适合如果您喜欢,可以点击上面的关注二字。后续会为您提供更多有价值的内容。今天分享,送给长辈,还在纠结华为和vivo吗?这4款机型最适合。第一款华为畅享50Pro参考价格1499(128最适合孩子吃的食物,牛肉未上榜,豆腐排倒数第一,建议了解最适合孩子吃的食物,牛肉未上榜,豆腐排倒数第一,建议了解春天是孩子长个的黄金期,长个时候,家长一定要注意孩子的营养和健康。春天天气好的时候,多带孩子出门活动,吸收太阳光,促进钙的吸联想公布GeekProG5000系列游戏本,性能小金刚本文转自IT之家作者孤城联想今日公布GeekProG5000系列游戏本,其定位预计比拯救者系列更低。联想GeekPro品牌此前用于台式机,其定位大概在商用主机和游戏主机之间。现在,漯河源汇区嵩山路学校举行爱国主义教育活动为深切缅怀英雄烈士不朽功勋,充分发挥英烈文化立德树人的重要作用,树立崇尚英雄缅怀先烈学习先烈的良好风尚,2023年2月16日上午,漯河市源汇区嵩山路学校在大礼堂举行永远的纪念英烈精美国敢对中东下手,为何对朝鲜却无可奈何?其中有5个原因!在阅读此文之前,麻烦您点击一下关注,既方便您进行讨论和分享,又能给您带来不一样的参与感,感谢您的支持美国敢对中东下手,为何对朝鲜却无可奈何?其中有5个原因!自从苏联解体后,美国登上俗话说欠债还钱,在唐朝,民间的损害赔偿制度是什么样的呢?阅读此文前,麻烦您点击一下关注,既方便您进行讨论与分享,又给您带来不一样的参与感,感谢您的支持。引言唐朝是古代中国历史文明发展的黄金时期,唐朝的经济发展是古代中国的转型和发展时期,
入春后才发现不兴阔腿裤了!满大街都在穿树杈裤,显高显瘦选对一条裤子,可以横穿几个季节,即使换季期间也不用担心没有裤子可搭配。入春后,厚重笨拙的加绒裤都收起来了,接下来要换上清爽利落的单裤去搭配啦,还在穿阔腿裤的你们就赶紧淘汰了吧,今年水变油终成现实?一杯水行驶40公里,这次可是真的了!水变油终成现实?今天看到一个消息,中国一家公司发布了一辆氢能自行车,一杯水制成的氢就能驱动自行车行驶40公里,未来氢能自行车会取代电动自行车吗?发布这款自行车的是永安行科技股份有限批评年轻人不上进不上课只上香?请先认清现实,再认清传统文化23年,寺庙游火了,数据显示,寺庙相关景区门票订单量同比增长3102月以来预订门票的人群中,90后00后占比接近50从19年到23年,寺庙在社交平台而今的搜索量增长了368倍。知名张融亿3。21黄金原油今日最新走势分析,黄金原油操作建议黄金行情走势目前避险情绪仍未完全消退,金价短线下行空间仍受到限制,投资者一方面关注欧美银行业的进一步消息,一方面在等待本周美联储利率决议。人们对美联储可能暂停加息的预期越来越高,对2023年3月20日钾肥行情钾肥氯化钾钾肥市场价格持续走低,仍不断有低价的传闻,且市场贸易商手中的货源有限,多集中在大型贸易商手中,短线操作为主。边贸新货源持续抵达,且后面期货多以62白粉为主,价格不断下行,瑞信银行170亿美元债券先于股票一夜清零A股市场回顾价万得全A跌0。37量10578亿风格科创50领先,中证200垫后行业传媒领先,通信垫后行业聚焦昨日传媒行业涨幅第一。事件2023年3月,百度文心一言正式发布,截至3月新华全媒丨旺起来快起来忙起来!空天陆视角透视经济复苏新华社天津3月20日电(记者梁姊王井怀)今年以来,随着疫情防控较快平稳转段,稳经济政策效果持续显现,我国经济循环加快畅通,多项经济指标回升向好,经济运行整体呈现企稳回升态势。从一组宜居宜业新栖霞,锚定打造南京高质量发展新高地推进55个省市重大项目开工城建投资全市第一一批断头路打通五拼五比晒五榜首榜斩获拼经济比贡献红旗激励2023年开年,南京市栖霞区奔跑开局,轰鸣主引擎,竞速产业快车道,展示出最美新栖霞企业能源管理的概念目的和方法企业能源管理是指对企业能源的生产分配转换和消耗的全过程进行科学的计划组织检查控制和监督工作,以提高能源利用效率,降低单位产品能源消耗,提高经济效益,降低CO2排放量。企业能源管理是四川遂宁市台资企业全力实现一季度开门红车间内,工人们正在开足马力赶制电路板,力争取得首季开门红。(中国台湾网发)中国台湾网3月21日讯连日来,四川遂宁市台资企业纷纷开足马力,抢进度,抓生产,全力以赴拼经济,奋力夺取一季党建引领抓共建工商携手促发展走进福州市工商行业会员企业为深入学习贯彻党的二十大和全国两会精神,进一步促进两个循环扩大消费和助推先进制造业高质量发展,把实体经济做优做强,坚决落实省委两新工委和省工信厅行业党委的2023年党建工作部署3月