关于i++的疑问

通过JVM javap -c 查看字节码执行步骤了解了i++之后,衍生了一个问题:

int num1=50;

num1++*2执行的是imul(将栈顶两int类型数相乘,结果入栈),

那么

 1. 计算机是如何计算的?
2. 为什么是栈顶的两个数相乘?
3. 为什么这样规定?
4. 计算完之后这个栈栈顶往下是100 2 50 三个数吗?
5. 计算完之后这个栈有发生了什么?

回顾什么是机器数、真值、原码、反码、补码

此处完全转载知乎博文:原码、反码、补码?这样理解很简单

1、机器数

一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,用一个数的最高位存放符号, 正数为0, 负数为1.

比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011

那么,这里的 00000011 和 10000011 就是机器数。

2、真值

因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3

而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值

0000 0001的真值 = +000 0001 = +1
1000 0001的真值 = –000 0001 = –1

3、原码

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:

[+1]原 = 0000 0001
[-1]原 = 1000 0001

因为第一位是符号位,

所以8位二进制数的取值范围就是:

[1111 1111 ,0111 1111]
即 [-127 , 127]

原码是人脑最容易理解和计算的表示方式.

4、反码

正数的反码是其本身

负数的反码是在其原码的基础上,

符号位不变,其余各个位取反.

[+1] =[00000001]原 = [00000001]反
[-1] =[10000001]原 = [11111110]反

可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

5、补码

正数的补码就是其本身

负数的补码是在其原码的基础上,

符号位不变, 其余各位取反,

最后+1. (即在反码的基础上+1)

[+1] = [00000001]原
= [00000001]反
= [00000001]补 [-1] =[10000001]原
= [11111110]反
= [11111111]补

对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值

计算机是如何计算的?

在计算机中,数字都是用补码来存储的,而一个字节的数字,规定1000 0000就是-128

1.假如计算机采用原码计算:

1-1=1+(-1)=0000 0001[原] + 1000 0001[原]
=1000 00010[原]
=-2 计算机中每个字节byte有8位,最高位是符号位

但是结果是-2,明显按照这种计算方式(人的计算方式)存在问题,这种原码计算方式在人看来很好区分,但是机器不行.这也就是为何计算机内部不使用原码表示一个数

2.假如计算机采用反码计算:

 1-1=1+(-1)
=0000 0001[原] + 1000 0001[原]
=0000 0001[反] + 1111 1110[反]
=1111 1111[反]
=1000 0000[原]
=-0

结果正确,但是0带符号是没有任何意义的. 而且会有0000 0000[原]和1000 0000[原]两个编码表示0

3.假如计算机采用补码计算:

 1-1=1+(-1)
=0000 0001[原] + 1000 0001[原]
=0000 0001[反] + 1111 1110[反]
=0000 0001[补] + 1111 1111[补]
=0000 0000[补]
=0000 0000[原]

这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128

 (-1) + (-127)
= [1000 0001]原 + [1111 1111]原
= [1111 1111]补 + [1000 0001]补
= [1000 0000]补

-1-127的结果应该是-128, 在用补码运算的结果中, 1000 0000[补] 就是-128

但是实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示

so,使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数.

这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127]


分析下乘法计算a乘以x

计算机中乘法计算的规则如下

对于计算机而言,左移一位代表乘以2,右移一位代表除以2。所以,对于a乘以x而言,只是将a左移x为1的位并累加即可

举例:8*9= 0000 1000 * 0000 1001

  1. 9的第1位是1,\(1*2^0\),so,8左移0位,结果是 0000 1000=8
  2. 9的第2位是0,\(0*2^1\),so,位数为0,不运算,结果是 0=0
  3. 9的第3位是0,\(0*2^2\),so,位数为0,不运算,结果是 0=0
  4. 9的第4位是1,\(1*2^3\),so,8左移3位,结果是 0100 0000=64

    累加后 8+0+0+64=72

那么补码一位乘???除法?

尚未掌握,留待以后...

运行时栈帧分析

在这之前先看一段关于JVM虚拟机栈的描述:(from《深入理解Java虚拟机》2.2.3)

与程序计数器一样,JVM虚拟机栈也是线程私有的,他的生命周期与线程相同。虚拟机栈描述的是Java方
法执行的内存模型,每个方法在执行的时候都会创建一个栈帧用于存储局部变量表、操作数栈、动态链
接、方法返回地址等信息。每一个方法从调用直至执行完的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

ok,让我们来分析下运行时栈帧结构


栈帧结构包含以下几种:

1.局部变量表

2.操作数栈

又叫操作栈。

  当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写人和提取内容,也就是出栈/入栈操作。例如,在做算术运算的时候是通过操作数栈来进行的,又或者在调用其他方法的时候是通过操作数栈来进行参数传递的。

 

  举个例子,整数加法的字节码指令 iadd 在运行的时候操作数栈中最接近栈顶的两个元素已经存人了两个 int 型的数值,当执行这个指令时, 会将这两个 int 值出栈并相加,然后将相加的结果人栈。

  

3.动态链接

4.方法返回地址

  无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执背状态。一般来说,方法正常退出时,调用者的PC 计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。

  

  方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:

恢复上层方法的局部变量表和操作数栈,
把返回值(如果有的话)压入调用者栈帧的操作数中,
调整PC计数器的值以指向方法调用指令后面的一条指令等。

5.附加信息

此时再来看之前的几个问题

2. 为什么是栈顶的两个数相乘?

3. 为什么这样规定?

4. 计算完之后这个栈栈顶往下是100 2 50 三个数吗?

5. 计算完之后这个栈有发生了什么?


下面通过分析运行时操作数栈和局部变量的编号来解释上边的几个问题(from 深入Java虚拟机8.4.1)

程序:

 public class StrTest {
public int clac(){
int a=100;
int b=200;
int c=300;
return (a+b)*c;
}
}

javap -c StrTest.class得到 字节码指令

D:\notepadd++cache>javap -c StrTest.class
Compiled from "StrTest.java"
public class StrTest {
public StrTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int clac();
Code:
0: bipush 100
2: istore_1
3: sipush 200
6: istore_2
7: sipush 300
10: istore_3
11: iload_1
12: iload_2
13: iadd
14: iload_3
15: imul
16: ireturn
}

分析图:

   

      

   

总结

2. 为什么是栈顶的两个数相乘?

两数相乘是从操作数栈栈顶取数进行计算

3. 为什么这样规定?

why? 可能要很久以后才会理解或者想通吧!!!

4. 计算完之后这个栈栈顶往下是100 2 50 三个数吗?

计算相乘时两个数字从栈顶弹出,并把计算记过推入栈顶,所以栈中只有100

5. 计算完之后这个栈有发生了什么?

计算完成后此栈帧退出,把返回值压入调用这个方法的操作数栈


没想到一个自增搞出这多事。

计算机是如何计算的、运行时栈帧分析(神奇i++续)的更多相关文章

  1. Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)

    Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...

  2. 深入理解java虚拟机(十) Java 虚拟机运行时栈帧结构

    运行时栈帧结构 栈帧(Stack Frame) 是用于虚拟机执行时方法调用和方法执行时的数据结构,它是虚拟栈数据区的组成元素.每一个方法从调用到方法返回都对应着一个栈帧入栈出栈的过程. 每一个栈帧在编 ...

  3. java虚拟机规范-运行时栈帧

    前言 java虚拟机是java跨平台的基石,本文的描述以jdk7.0为准,其他版本可能会有一些微调. 引用 java虚拟机规范 java虚拟机规范-运行时数据区 java内存运行时的栈帧结构 java ...

  4. 【转载】深入理解Java虚拟机笔记---运行时栈帧结构

    栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法的局部变量表,操作 ...

  5. x86_64 Linux 运行时栈的字节对齐

    前言 C语言的过程调用机制(即函数之间的调用)的一个关键特性(起始大多数编程语言也是如此)都是使用了栈数据结构提供的后进先出的内存管理原则.每一个函数的栈空间被称为栈帧,一个栈帧上包含了保存的寄存器. ...

  6. c函数调用过程原理及函数栈帧分析

    转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707       今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...

  7. Java运行时数据区域分析

    Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结 ...

  8. Java 虚拟机中的运行时数据区分析

    本文基于 JDK1.8 阐述分析 运行过程 我们都知道 Java 源文件通过编译器编译后,能产生相应的 .Class 文件,也就是字节码文件.而字节码文件通过 Java 虚拟机中的解释器,编译成特定机 ...

  9. 图文并茂-超详解 CS:APP: Lab3-Attack(附带栈帧分析)

    CS:APP:Lab3-ATTACK 0. 环境要求 关于环境已经在lab1里配置过了.lab1的连接如下 实验的下载地址如下 说明文档如下 http://csapp.cs.cmu.edu/3e/at ...

随机推荐

  1. linux jar 启动shell 脚本

    #!/bin/bash APP_NAME=/data/wwwroot/app.jar #使用说明,用来提示输入参数 usage() { echo "Usage: sh app.sh [sta ...

  2. Django中defer和only区别

    defer('id', 'name'):取出对象,字段除了id和name都有 only('id', 'name'):取出对象, 只有id和name ret=models.Author.objects. ...

  3. sparkRDD:第4节 RDD的依赖关系;第5节 RDD的缓存机制;第6节 DAG的生成

    4.      RDD的依赖关系 6.1      RDD的依赖 RDD和它依赖的父RDD的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency ...

  4. 树 插件 ztree 的基本用法

    因业务需要 用到 ztree 插件 第一次用tree插件上手有点难度 官网 http://www.treejs.cn/v3/main.php#_zTreeInfo 第一步:初始化树,树的所有数据从后台 ...

  5. IntelliJ IDEA 破解之后,用了一段时间后,打开软件提示 no suitable licenses left on the license server

    IntelliJ IDEA 破解之后,用了一段时间后,打开软件提示 no suitable licenses left on the license server 需要让我们重新注册,原来是之前的地址 ...

  6. 「HNOI2010」弹飞绵羊

    「HNOI2010」弹飞绵羊 传送门 考虑分块. 每一个位置 \(i\) ,记 \(to[i]\) 表示从这个位置一直往右跳回落在哪个位置. 然后修改的时候直接暴改,查询也是暴跳,复杂度 \(O(n ...

  7. CSS - 滑动门技术

    1. 概念: 1.1 为了使各种特殊形状的背景能够自适应元素中文本内容的多少,出现了CSS滑动门技术. 1.2 使各种特殊形状的背景能够自由拉伸滑动,以适应元素内部的文本内容,可用性更强. 1.3 最 ...

  8. mysql odbc 配置详解

    1.安装mysql 以及mysql odbc 要注意自己的版本 版本都要统一(32位 或者64位) 2.出现的error 1989 126错误代码 Error 1918. Error installi ...

  9. 为安卓手机刷上手机kali系统Nethunter

    kali Nethunter是一个装在手机上的kali,集成了kali的工具包,hid,无线攻击等等. 本文主要叙述如何安装此系统并正确的配置.不让你走弯路. 首先我们拒绝傻瓜安装软件 kali ne ...

  10. Linux CentOS7 VMware 相对和绝对路径、cd命令、mkdir/rmdir、rm命令——笔记

    一. 相对和绝对路径 绝对路径是从/(也被称为根目录)开始的,比如/usr.cd /root/ pwd 注:判断用户当前所处的位置 相对路径是以 . 或 .. 开始的 二.cd命令 cd 是进入到当前 ...