第25篇-虚拟机对象操作指令之putstatic
之前已经介绍了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 前缀的说明如下:
- 确保被修饰指令执行的原子性;
- 禁止该指令与前面和后面的读写指令重排序;
- 指令执行完后把写缓冲区的所有数据刷新到内存中(这样这个指令之前的其他修改对所有处理器可见)。
在所有的 X86 CPU 上都具有锁定一个特定内存地址的能力,当这个特定内存地址被锁定后,它就可以阻止其他的系统总线读取或修改这个内存地址。这种能力是通过 lock 指令前缀再加上下面的汇编指令来实现的。当使用 lock 指令前缀时,它会使 CPU 宣告一个 lock# 信号,这样就能确保在多处理器系统或多线程竞争的环境下互斥地使用这个内存地址。当指令执行完毕,这个锁定动作也就会消失。
推荐阅读:
第2篇-JVM虚拟机这样来调用Java主类的main()方法
第13篇-通过InterpreterCodelet存储机器指令片段
第20篇-加载与存储指令之ldc与_fast_aldc指令(2)
如果有问题可直接评论留言或加作者微信mazhimazh
关注公众号,有HotSpot VM源码剖析系列文章!
第25篇-虚拟机对象操作指令之putstatic的更多相关文章
- 第24篇-虚拟机对象操作指令之getfield
getfield指令表示获取指定类的实例域,并将其值压入栈顶.其格式如下: getstatic indexbyte1 indexbyte2 无符号数indexbyte1和indexbyte2构建为(i ...
- 第23篇-虚拟机对象操作指令之getstatic
Java虚拟机规范中定义的对象操作相关的字节码指令如下表所示. 0xb2 getstatic 获取指定类的静态域,并将其值压入栈顶 0xb3 putstatic 为指定的类的静态域赋值 0xb4 ge ...
- Linux连接虚拟机及操作指令
Linux的安装(虚拟机环境)与基础配置 一.背景 本文介绍如何安装虚拟机VMware以及如果在虚拟机上安装Linux系统以及Linux安装完毕之后的基础配置 需要准备的东西有VMware以及Li ...
- 深入理解JVM(1)——栈和局部变量操作指令
将常量压入栈的指令 aconst_null 将null对象引用压入栈iconst_m1 将int类型常量-1压入栈iconst_0 将int类型常量0压入栈iconst_1 将int类型常量1压入栈i ...
- 前端学PHP之面向对象系列第五篇——对象操作
× 目录 [1]对象克隆 [2]对象比较[3]对象串行化[4]json 前面的话 本文主要介绍面向对象中的一些对象操作 对象克隆 对象复制,又叫对象克隆,可以通过 clone 关键字来完成 在多数情况 ...
- JVM(四)-虚拟机对象
概述: 上一篇文章,介绍了虚拟机类加载的过程,那么类加载好之后,虚拟机下一步该干什么呢.我们知道java是面向对象的编程语言,所以对象可以说是java'的灵魂,这篇文章我们就来介绍 虚拟机是如何创建对 ...
- HotSpot虚拟机对象相关内容
一.对象的创建 1.类加载检查 普通对象的创建过程:虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化 ...
- 深入理解Java虚拟机读书笔记1----Java内存区域与HotSpot虚拟机对象
一 Java内存区域与HotSpot虚拟机对象 1 Java技术体系.JDK.JRE? Java技术体系包括: · Java程序设计语言: · 各种硬件平台上的 ...
- jquery实现点击展开列表同时隐藏其他列表 js 对象操作 对象原型操作 把一个对象A赋值给另一个对象B 并且对象B 修改 不会影响 A对象
这篇文章主要介绍了jquery实现点击展开列表同时隐藏其他列表的方法,涉及jquery鼠标事件及节点的遍历与属性操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下 本文实例讲述了jquery实现点击 ...
随机推荐
- 为数不多的人知道的 Kotlin 技巧及解析
文章中没有奇淫技巧,都是一些在实际开发中常用,但很容易被我们忽略的一些常见问题,源于平时的总结,这篇文章主要对这些常见问题进行分析. 这篇文章主要分析一些常见问题的解决方案,如果使用不当会对 性能 和 ...
- 字节跳动Android春招,三轮面试,夺命连环问,心态崩了
我是春招参加字节面试的,现在已经入职俩月啦,当时没有及时记录下来拖到现在...我尽量回忆当时的内容希望能帮到大家. 投的部门是深圳字节影像,不得不说这个部门的效率,上午投下午就接到hr的电话约面试时间 ...
- UNIX环境高级编程APUE练习4.6-实现类似cp(1)的程序,保留文件中的空洞
1 题面 编写类似cp(1)的程序,它复制包含空洞的文件,但是不将字节0写到输出文件中去. 2 基本思路 首先要搞清楚空洞的性质以判断一个文件是否有空洞,以及空洞的位置 知道了空洞的位置之后,读到源文 ...
- rollup 开发环境搭建
rollup 开发环境搭建 初始化项目使用lerna管理项目 使用npm init 初始化项目 npm init -y 安装lerna并初始化项目 npm install lerna --save-d ...
- Create Virtual Machines with Vagrant and Puppet
Create the following puppet manifest and start VM with vagrant, you get a base production environmen ...
- 华为应用市场更新APP多次被拒
最近公司的APP发布了新版本,只进行了线上bug的修复,基本没改什么主体业务功能.各大应用市场都顺利更新上架,但是国货之光华为,被闷了几次.拒来拒去,就是那些反复的内容.内容一般如下: 经检测发现,您 ...
- 【笔记】衡量线性回归法的指标 MSE,RMS,MAE以及评价回归算法 R Square
衡量线性回归法的指标 MSE,RMS,MAE以及评价回归算法 R Square 衡量线性回归法的指标 对于分类问题来说,我们将原始数据分成了训练数据集和测试数据集两部分,我们使用训练数据集得到模型以后 ...
- CPU 进程 线程 关系与区别
- 【转】关于DNS不得不说的一些事
转自:https://www.cnblogs.com/rjzheng/p/11395695.html 引言 今天我们来聊聊DNS.所谓域名系统(Domain Name System缩写DNS,Doma ...
- Spark入门:Spark运行架构(Python版)
此文为个人学习笔记如需系统学习请访问http://dblab.xmu.edu.cn/blog/1709-2/ 基本概念 * RDD:是弹性分布式数据集(Resilient Distributed ...