之前已经介绍了getstatic与getfield指令的汇编代码执行逻辑,这一篇介绍putstatic指令的执行逻辑,putfield将不再介绍,大家可以自己去研究,相信大家有这个实力。

putstatic指令为指定类的静态域赋值。字节码指令的格式如下:

putstatic indexbyte1 indexbyte2

无符号数indexbyte1和indexbyte2构建为(indexbyte1<<8)|indexbyte2,该索引所指向的运行时常量池项应当是一个字段的符号引用。

指令的模板定义如下:

def(Bytecodes::_putstatic           , ubcp|____|clvm|____, vtos, vtos, putstatic           , f2_byte      );  

生成函数为putstatic(),函数的实现如下:

void TemplateTable::putstatic(int byte_no) {
putfield_or_static(byte_no, false);
}

调用TemplateTable::putfield_or_static()函数生成的机器指令对应的汇编代码如下:

0x00007fffe101ff90: movzwl 0x1(%r13),%edx
0x00007fffe101ff95: mov -0x28(%rbp),%rcx
0x00007fffe101ff99: shl $0x2,%edx
0x00007fffe101ff9c: mov 0x10(%rcx,%rdx,8),%ebx
0x00007fffe101ffa0: shr $0x18,%ebx
0x00007fffe101ffa3: and $0xff,%ebx
// 是否已经对putstatic指令进行了连接,如果已经连接,则跳转到resolved
0x00007fffe101ffa9: cmp $0xb3,%ebx
0x00007fffe101ffaf: je 0x00007fffe102004e

调用TemplateTable::resolve_cache_and_index()函数生成如下汇编代码:

// 执行到这里,说明字段还没有连接
0x00007fffe101ffb5: mov $0xb3,%ebx // 调用MacroAssembler::call_VM()函数生成如下代码,
// 用来执行InterpreterRuntime::resolve_get_put()函数
0x00007fffe101ffba: callq 0x00007fffe101ffc4
0x00007fffe101ffbf: jmpq 0x00007fffe1020042
0x00007fffe101ffc4: mov %rbx,%rsi
0x00007fffe101ffc7: lea 0x8(%rsp),%rax
0x00007fffe101ffcc: mov %r13,-0x38(%rbp)
0x00007fffe101ffd0: mov %r15,%rdi
0x00007fffe101ffd3: mov %rbp,0x200(%r15)
0x00007fffe101ffda: mov %rax,0x1f0(%r15)
0x00007fffe101ffe1: test $0xf,%esp
0x00007fffe101ffe7: je 0x00007fffe101ffff
0x00007fffe101ffed: sub $0x8,%rsp
0x00007fffe101fff1: callq 0x00007ffff66b567c
0x00007fffe101fff6: add $0x8,%rsp
0x00007fffe101fffa: jmpq 0x00007fffe1020004
0x00007fffe101ffff: callq 0x00007ffff66b567c
0x00007fffe1020004: movabs $0x0,%r10
0x00007fffe102000e: mov %r10,0x1f0(%r15)
0x00007fffe1020015: movabs $0x0,%r10
0x00007fffe102001f: mov %r10,0x200(%r15)
0x00007fffe1020026: cmpq $0x0,0x8(%r15)
0x00007fffe102002e: je 0x00007fffe1020039
0x00007fffe1020034: jmpq 0x00007fffe1000420
0x00007fffe1020039: mov -0x38(%rbp),%r13
0x00007fffe102003d: mov -0x30(%rbp),%r14
0x00007fffe1020041: retq 0x00007fffe1020042: movzwl 0x1(%r13),%edx
0x00007fffe1020047: mov -0x28(%rbp),%rcx
0x00007fffe102004b: shl $0x2,%edx

接下来生成的汇编代码如下:

// ---- resolved ----

// 执行如下代码时,表示字段已经连接完成

0x00007fffe102004e: mov    0x20(%rcx,%rdx,8),%rbx
0x00007fffe1020053: mov 0x28(%rcx,%rdx,8),%eax
0x00007fffe1020057: mov 0x18(%rcx,%rdx,8),%rcx
0x00007fffe102005c: mov 0x70(%rcx),%rcx
0x00007fffe1020060: mov %eax,%edx
// 将_flags向右移动21位,判断是否有volatile关键字
0x00007fffe1020062: shr $0x15,%edx
0x00007fffe1020065: and $0x1,%edx
// 将_flags向右移动28位,剩下TosState
0x00007fffe1020068: shr $0x1c,%eax // 如果不为btos,则跳转到notByte
0x00007fffe102006b: and $0xf,%eax
0x00007fffe102006e: jne 0x00007fffe1020083 // btos // 将栈顶的值存储到%eax中,这个值会写入到对应的字段中
0x00007fffe1020074: mov (%rsp),%eax
0x00007fffe1020077: add $0x8,%rsp
// %rcx为_java_mirror,%rbx为_f2,表示域在类中的偏移
0x00007fffe102007b: mov %al,(%rcx,%rbx,1)
0x00007fffe102007e: jmpq 0x00007fffe10201be // 跳转到Done
// -- notByte --
// 如果不为atos,则跳转到notObj
0x00007fffe1020083: cmp $0x7,%eax
0x00007fffe1020086: jne 0x00007fffe1020130 // atos
// 将栈顶的值弹出到%rax中,这个值将用来更新对应字段的值
0x00007fffe102008c: pop %rax
// ...
// 将值更新到对应的字段上
0x00007fffe1020115: mov %eax,(%rcx,%rbx,1)
// 其中的0x9是CardTableModRefBS::card_shift,shr表示逻辑右移,由于%rcx指向的是
// java.lang.Class实例的首地址,向右移后%rcx就算出了卡表的索引
0x00007fffe1020118: shr $0x9,%rcx
// 地址常量$0x7fffe07ff000表示卡表的基地址
0x00007fffe102011c: movabs $0x7fffe07ff000,%r10
// 将对应的卡表项标记为脏,其中常量0x0就表示是脏卡
0x00007fffe1020126: movb $0x0,(%r10,%rcx,1)
0x00007fffe102012b: jmpq
0x00007fffe10201be // 跳转到Done
// ---- notObj ----
// 如果不为itos,那么跳转到notInt
0x00007fffe1020130: cmp $0x3,%eax
0x00007fffe1020133: jne 0x00007fffe1020148 // itos
0x00007fffe1020139: mov (%rsp),%eax
// 如果不为ctos,则跳转到notChar
0x00007fffe102013c: add $0x8,%rsp
0x00007fffe1020140: mov %eax,(%rcx,%rbx,1)
0x00007fffe1020143: jmpq 0x00007fffe10201be // 跳转到Done
0x00007fffe1020148: cmp $0x1,%eax
0x00007fffe102014b: jne 0x00007fffe1020161 // ctos
0x00007fffe1020151: mov (%rsp),%eax
0x00007fffe1020154: add $0x8,%rsp
0x00007fffe1020158: mov %ax,(%rcx,%rbx,1)
0x00007fffe102015c: jmpq 0x00007fffe10201be // 跳转到Done
0x00007fffe1020161: cmp $0x2,%eax
0x00007fffe1020164: jne 0x00007fffe102017a // stos
0x00007fffe102016a: mov (%rsp),%eax
0x00007fffe102016d: add $0x8,%rsp
0x00007fffe1020171: mov %ax,(%rcx,%rbx,1)
0x00007fffe1020175: jmpq 0x00007fffe10201be // 跳转到Done
0x00007fffe102017a: cmp $0x4,%eax
0x00007fffe102017d: jne 0x00007fffe1020194 // ltos
0x00007fffe1020183: mov (%rsp),%rax
0x00007fffe1020187: add $0x10,%rsp
0x00007fffe102018b: mov %rax,(%rcx,%rbx,1)
0x00007fffe102018f: jmpq 0x00007fffe10201be // 跳转到Done
0x00007fffe1020194: cmp $0x5,%eax
0x00007fffe1020197: jne 0x00007fffe10201b0 // ftos
0x00007fffe102019d: vmovss (%rsp),%xmm0
0x00007fffe10201a2: add $0x8,%rsp
0x00007fffe10201a6: vmovss %xmm0,(%rcx,%rbx,1)
0x00007fffe10201ab: jmpq 0x00007fffe10201be // 跳转到Done // dtos
0x00007fffe10201b0: vmovsd (%rsp),%xmm0
0x00007fffe10201b5: add $0x10,%rsp
0x00007fffe10201b9: vmovsd %xmm0,(%rcx,%rbx,1) // ---- Done ---- 0x00007fffe10201be: test %edx,%edx
0x00007fffe10201c0: je 0x00007fffe10201cb
0x00007fffe10201c6: lock addl $0x0,(%rsp) // ---- notVolatile ---- 
  

在如上代码中,最值得关注的2个点如下:

(1)更新引用字段时,通过屏障将对应的卡表项标记为脏,这样可在GC过程中扫描脏卡就可将活跃对象标记出来而不会造成遗漏;

(2)当字段有volatile关键字修饰时,需要填写lock指令前缀,这个前缀在之前介绍x86-64机器指令时没有介绍过,这里摘抄一下别人对此指令的介绍:

Intel手册对 lock 前缀的说明如下:

  1. 确保被修饰指令执行的原子性;
  2. 禁止该指令与前面和后面的读写指令重排序;
  3. 指令执行完后把写缓冲区的所有数据刷新到内存中(这样这个指令之前的其他修改对所有处理器可见)。

在所有的 X86 CPU 上都具有锁定一个特定内存地址的能力,当这个特定内存地址被锁定后,它就可以阻止其他的系统总线读取或修改这个内存地址。这种能力是通过 lock 指令前缀再加上下面的汇编指令来实现的。当使用 lock 指令前缀时,它会使 CPU 宣告一个 lock# 信号,这样就能确保在多处理器系统或多线程竞争的环境下互斥地使用这个内存地址。当指令执行完毕,这个锁定动作也就会消失。

推荐阅读:

第1篇-关于JVM运行时,开篇说的简单些

第2篇-JVM虚拟机这样来调用Java主类的main()方法

第3篇-CallStub新栈帧的创建

第4篇-JVM终于开始调用Java主类的main()方法啦

第5篇-调用Java方法后弹出栈帧及处理返回结果

第6篇-Java方法新栈帧的创建

第7篇-为Java方法创建栈帧

第8篇-dispatch_next()函数分派字节码

第9篇-字节码指令的定义

第10篇-初始化模板表

第11篇-认识Stub与StubQueue

第12篇-认识CodeletMark

第13篇-通过InterpreterCodelet存储机器指令片段

第14篇-生成重要的例程

第15章-解释器及解释器生成器

第16章-虚拟机中的汇编器

第17章-x86-64寄存器

第18章-x86指令集之常用指令

第19篇-加载与存储指令(1)

第20篇-加载与存储指令之ldc与_fast_aldc指令(2)

第21篇-虚拟机字节码之运算指令

第22篇-虚拟机字节码指令之类型转换

第23篇-虚拟机对象操作指令之getstatic

第24篇-虚拟机对象操作指令之getfield

如果有问题可直接评论留言或加作者微信mazhimazh

关注公众号,有HotSpot VM源码剖析系列文章!

  

 

第25篇-虚拟机对象操作指令之putstatic的更多相关文章

  1. 第24篇-虚拟机对象操作指令之getfield

    getfield指令表示获取指定类的实例域,并将其值压入栈顶.其格式如下: getstatic indexbyte1 indexbyte2 无符号数indexbyte1和indexbyte2构建为(i ...

  2. 第23篇-虚拟机对象操作指令之getstatic

    Java虚拟机规范中定义的对象操作相关的字节码指令如下表所示. 0xb2 getstatic 获取指定类的静态域,并将其值压入栈顶 0xb3 putstatic 为指定的类的静态域赋值 0xb4 ge ...

  3. Linux连接虚拟机及操作指令

    Linux的安装(虚拟机环境)与基础配置   一.背景 本文介绍如何安装虚拟机VMware以及如果在虚拟机上安装Linux系统以及Linux安装完毕之后的基础配置 需要准备的东西有VMware以及Li ...

  4. 深入理解JVM(1)——栈和局部变量操作指令

    将常量压入栈的指令 aconst_null 将null对象引用压入栈iconst_m1 将int类型常量-1压入栈iconst_0 将int类型常量0压入栈iconst_1 将int类型常量1压入栈i ...

  5. 前端学PHP之面向对象系列第五篇——对象操作

    × 目录 [1]对象克隆 [2]对象比较[3]对象串行化[4]json 前面的话 本文主要介绍面向对象中的一些对象操作 对象克隆 对象复制,又叫对象克隆,可以通过 clone 关键字来完成 在多数情况 ...

  6. JVM(四)-虚拟机对象

    概述: 上一篇文章,介绍了虚拟机类加载的过程,那么类加载好之后,虚拟机下一步该干什么呢.我们知道java是面向对象的编程语言,所以对象可以说是java'的灵魂,这篇文章我们就来介绍 虚拟机是如何创建对 ...

  7. HotSpot虚拟机对象相关内容

    一.对象的创建 1.类加载检查 普通对象的创建过程:虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化 ...

  8. 深入理解Java虚拟机读书笔记1----Java内存区域与HotSpot虚拟机对象

    一 Java内存区域与HotSpot虚拟机对象 1 Java技术体系.JDK.JRE?     Java技术体系包括:         · Java程序设计语言:         · 各种硬件平台上的 ...

  9. jquery实现点击展开列表同时隐藏其他列表 js 对象操作 对象原型操作 把一个对象A赋值给另一个对象B 并且对象B 修改 不会影响 A对象

    这篇文章主要介绍了jquery实现点击展开列表同时隐藏其他列表的方法,涉及jquery鼠标事件及节点的遍历与属性操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下 本文实例讲述了jquery实现点击 ...

随机推荐

  1. 基于ivy的源代码调试方法

    项目PORJ_TEST是项目PROJ的测试项目.在它的ivy中引用了PROJ的jar包.由于PROJ正处于开发阶段,源代码更改频繁, 在运行PROJ_TEST中的测试时,需要进入PROJ的jar包内部 ...

  2. 【MySQL】自定义数据库连接池和开源数据库连接池的使用

    数据库连接池的概念 数据库连接背景 数据库连接是一种关键的.有限的.昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性 ...

  3. JVM的GC机制

    JVM的GC机制 1. 什么对象会被回收 引用计数法:如果一个对象被引用一次,则记录引用次数加一,如果引用取消,则减一,当减到0时,需要被回收. 问题:循环引用,A引用B,B引用A,除此之外,已经无法 ...

  4. Emlog V6.0.0代码审计笔记

    前言 emlog是一套基于PHP和MySQL的博客及CMS建站系统. emlog v6.0.0存在后台SQL注入漏洞. 分析 官网下载emlog最新版v6.0.0,本地搭建. 前台功能不多,参数基本都 ...

  5. S3C2440—12.按键中断

    文章目录 一. 总体 二. CPSR设置 三. 中断源设置 四. 中断控制器设置 五. C中断处理函数 六. 汇编IRQ异常处理程序 七. 源码 一. 总体 要驱动按键中断控制LED亮灭,程序要进行如 ...

  6. 编写一个简单的COM组件

    参考网站:编写一个简单的COM组件_a ray of sunshine-CSDN博客 (1) 用MIDL编写.idl文件 //将以下代码保存成 IXIYIZ.idl 文件 //在命令行上进行编译,编译 ...

  7. 【spring 注解驱动开发】spring自动装配

    尚学堂spring 注解驱动开发学习笔记之 - 自动装配 自动装配 1.自动装配-@Autowired&@Qualifier&@Primary 2.自动装配-@Resource& ...

  8. Spring第一课:基于XML装配bean(四),三种实例化方式:默认构造、静态工厂、实例工厂

    Spring中基于XML中的装配bean有三种方式: 1.默认构造 2.静态工厂 3.实例工厂 1.默认构造 在我们在Spring的xml文件中直接通过:     <bean id=" ...

  9. struts2思想学习(一)

    OOP 面向对象编程 AOP 面向切面编程 而在struts2 处处体现了面向切面编程的思想(动态代理最典型)! 拦截器其实也是面向切面编程!拦截器切断了所有请求到action的操作 并做了很多的前提 ...

  10. Java第一阶段项目实训

    时间:2016-3-27 17:09 银行综合业务平台业务需求 1.首页  ---------------银行综合业务平台------------------- 1开户     2登录    3.退出 ...