字节码详解x=x++

遇到这样一个八股之前被硬控很久,最近在深入学习字节码指令后豁然开朗,因此想从字节码+jvm底层图解的角度来解释这个问题,帮助日后同样被这个问题困惑而又看不懂复杂字节码的人搞明白这个问题

原八股题如下

 public static void main(String[] args) {
        int x=0;
        for (int i = 0; i < 10; i++) {
            x=x++;
        }
        System.out.println(x);//最终打印x为0
    }

问:为什么x的值未发生变化?

下面我将从四个例子来循序渐进讲明白这个问题

我们利用这个简单的demo来分析(初始化x,y之后的四行语句分别对应下文的四条分析)

 public static void main(String[] args) {
        int x=0;
        int y=0;
     	x=x+1;	//情况1
        x++;	//情况2
     	y=x++;	//情况3
     	x=x++;	//情况4
    }

先来从内存的角度看看初始化x和y后,赋值语句之前发生了什么:

jvm加载一个class字节码文件后会将字节码指令加载到方法区中,同时根据字节码的信息在当前线程栈帧中创建对应大小的局部变量表操作数栈,这里我们要对这两个结构比较敏感,因为下文就是围绕这二者展开

  • 局部变量表:存储变量的值,会为我们定义的变量开辟对应空间,运行过程中该变量的值都是记录在这里
  • 操作数栈:临时存储指令的输入参数和计算结果。在对变量执行一些操作时,比如int的加减乘除等,都需要将数据加载到操作数栈中进行操作 alt

比如这两条字节码指令执行的操作--int x=0;

0 iconst_0	//对应图中的 1.将常数0加载到操作数栈
1 istore_1	//对应图中   2.将栈顶的值(出栈)赋值给槽1(也就是变量x)

alt

大致了解这两个结构后再来跟进一步看其他赋值语句

x=x+1

先来看看常规的x+1赋值给x在底层是如何操作的

反编译后的字节码

0 iconst_0	
1 istore_1
2 iconst_0
3 istore_2	//以上4条是将0,0分别赋值给局部变量表的x,y实现初始化,对应x=0;y=0;
4 iload_1	//对应图中的 1.取出x的值到操作数栈
5 iconst_1	//对应图中的 2.加载常数1到操作数栈
6 iadd		//对应图中 3.相加后的值存到栈中
7 istore_1	//对应图中 4.将栈顶的和赋值给局部变量表的x
8 return

alt

可以看到,x=x+1是先将x的值从局部变量表中取出压入操作数栈,然后在将常数1压入操作数栈,然后执行iadd将栈顶的两个元素相加(这里iadd的操作其实是先将栈顶的两个元素弹出相加,然后再将和压入栈中),然后赋值给局部变量表的x

x++

0 iconst_0
1 istore_1
2 iconst_0
3 istore_2		//此前操作是给局部变量表中的a,b
4 iinc 1 by 1	//这条innc会在局部变量表中将a的值直接+1
7 return

x++这条语句编译后对应一句字节码,即iinc,这条指令的作用是将局部变量表中的值直接+1.而不用经过操作数栈

这是jvm做出的一个优化,可以看到相比x=x+1,自增操作不用频繁出入栈,更加高效,而这也正是问题根源

alt

y=x++/y=++x

编译后对应两句字节码iloadiinc

  • 如果是x++,就是先执行 iload将x的取值加载到操作数栈,然后执行iinc将局部变量表中的x自增+1;然后执行y的赋值操作,将操作数栈中的值存到局部变量表y的位置

  • ++x则是先自增x,后取出值到操作数栈然后赋值给y

(这里只演示y=x++,后者可以读者自己编译字节码查看)

0 iconst_0
1 istore_1
2 iconst_0
3 istore_2
4 iload_1   	//下图 1.局部变量表中取出x值到操作数栈
5 iinc 1 by 1	//下图 2.局部变量表x自增1
8 istore_2		//下图 3.将栈顶元素赋值给y
9 return

alt

x=x++

有了上面三个例子的铺垫,再来看x=x++

0 iconst_0
1 istore_1
2 iconst_0
3 istore_2
4 iload_1		//对应图中 1.将x的原值0取出到操作数栈
5 iinc 1 by 1	//对应图中 2.局部变量表中的x自增1后为1
8 istore_1		//对应图中 3.操作数栈中的0赋值给局部变量表的x
9 return

alt

  1. 执行iload将x的原值取出到操作数栈
  2. 对局部变量表中的x自增1
  3. 将操作数栈中的值赋值给局部变量表的x(即将原来的值重新赋值给x)

综上,x的值没有发生变化

从几个例子的字节码我们也可以得知这个结果就是自增指令iinc的特殊性-->局部变量表直接自增闹出的一个乌龙事件

而将原题中的x=x++换为x=++x打印结果就是 10了,可以结合上面的字节码就可以很容易想到原因了

注:这些是笔者查阅资料总结按照自己的思路总结的,不保证完全正确性,如有错误点恳请指出,共同交流,抱拳

#java#
fengdongnan的博客 文章被收录于专栏

记录fengdongnan的知识产出文档,欢迎大家来一起交流学习

全部评论
mark 字节码
点赞 回复 分享
发布于 2025-03-20 20:58 浙江
mark字节码详解
点赞 回复 分享
发布于 2025-03-17 10:14 江苏
赞一个
点赞 回复 分享
发布于 2025-03-16 21:46 浙江
mark 字节码
点赞 回复 分享
发布于 2025-03-16 21:39 广西

相关推荐

2025-12-18 18:23
深圳大学 前端工程师
程序员花海:实习和校招简历正确格式应该是教育背景+实习+项目经历+个人评价 其中项目经历注意要体现业务 实习经历里面的业务更是要自圆其说 简历模板尽可能保持干净整洁 不要太花哨的
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
2025-12-17 16:48
今天九点半到公司,我跟往常一样先扫了眼电脑,屁活儿没有。寻思着没事干,就去蹲了个厕所,回来摸出手机刷了会儿。结果老板刚好路过,拍了我一下说上班别玩手机,我吓得赶紧揣兜里。也就过了四十分钟吧,我的直属领导把我叫到小隔间,上来就给我一句:“你玩手机这事儿把老板惹毛了,说白了,你可以重新找工作了,等下&nbsp;HR&nbsp;会来跟你谈。”&nbsp;我当时脑子直接宕机,一句话都没憋出来。后面&nbsp;HR&nbsp;找我谈话,直属领导也在旁边。HR&nbsp;说我这毛病不是一次两次了,属于屡教不改,不光上班玩手机,还用公司电脑看论文、弄学校的事儿。我当时人都傻了,上班摸鱼是不对,可我都是闲得发慌的时候才摸啊!而且玩手机这事儿,从来没人跟我说过后果这么严重,更没人告诉我在公司学个习也算犯错!连一次口头提醒都没有,哪儿来的屡教不改啊?更让我膈应的是,昨天部门刚开了会,说四个实习生里留一个转正,让大家好好表现。结果今天我就因为玩手机被开了。但搞笑的是,开会前直属领导就把我叫去小会议室,明明白白告诉我:“转正这事儿你就别想了,你的学历达不到我们部门要求,当初招你进来也没打算给你这个机会。”合着我没入贵厂的眼是吧?可我都已经被排除在转正名单外了,摸个鱼至于直接把我开了吗?真的太离谱了!
rush$0522:转正名单没进,大概率本来就没打算留你
摸鱼被leader发现了...
点赞 评论 收藏
分享
评论
3
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务