深入拆解Java虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
立即订阅
27928 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 为什么我们要学习Java虚拟机?
免费
模块一:Java虚拟机基本原理 (12讲)
01 | Java代码是怎么运行的?
02 | Java的基本类型
03 | Java虚拟机是如何加载Java类的?
04 | JVM是如何执行方法调用的?(上)
05 | JVM是如何执行方法调用的?(下)
06 | JVM是如何处理异常的?
07 | JVM是如何实现反射的?
08 | JVM是怎么实现invokedynamic的?(上)
09 | JVM是怎么实现invokedynamic的?(下)
10 | Java对象的内存布局
11 | 垃圾回收(上)
12 | 垃圾回收(下)
模块二:高效编译 (12讲)
【工具篇】 常用工具介绍
13 | Java内存模型
14 | Java虚拟机是怎么实现synchronized的?
15 | Java语法糖与Java编译器
16 | 即时编译(上)
17 | 即时编译(下)
18 | 即时编译器的中间表达形式
19 | Java字节码(基础篇)
20 | 方法内联(上)
21 | 方法内联(下)
22 | HotSpot虚拟机的intrinsic
23 | 逃逸分析
模块三:代码优化 (10讲)
24 | 字段访问相关优化
25 | 循环优化
26 | 向量化
27 | 注解处理器
28 | 基准测试框架JMH(上)
29 | 基准测试框架JMH(下)
30 | Java虚拟机的监控及诊断工具(命令行篇)
31 | Java虚拟机的监控及诊断工具(GUI篇)
32 | JNI的运行机制
33 | Java Agent与字节码注入
模块四:黑科技 (3讲)
34 | Graal:用Java编译Java
35 | Truffle:语言实现框架
36 | SubstrateVM:AOT编译框架
尾声 (1讲)
尾声 | 道阻且长,努力加餐
深入拆解Java虚拟机
登录|注册

02 | Java的基本类型

郑雨迪 2018-07-23
如果你了解面向对象语言的发展史,那你可能听说过 Smalltalk 这门语言。它的影响力之大,以至于之后诞生的面向对象语言,或多或少都借鉴了它的设计和实现。
在 Smalltalk 中,所有的值都是对象。因此,许多人认为它是一门纯粹的面向对象语言。
Java 则不同,它引进了八个基本类型,来支持数值计算。Java 这么做的原因主要是工程上的考虑,因为使用基本类型能够在执行效率以及内存使用两方面提升软件性能。
今天,我们就来了解一下基本类型在 Java 虚拟机中的实现。
public class Foo {
public static void main(String[] args) {
boolean 吃过饭没 = 2; // 直接编译的话javac会报错
if (吃过饭没) System.out.println("吃了");
if (true == 吃过饭没) System.out.println("真吃了");
}
}
在上一篇结尾的小作业里,我构造了这么一段代码,它将一个 boolean 类型的局部变量赋值为 2。为了方便记忆,我们给这个变量起个名字,就叫“吃过饭没”。
赋值语句后边我设置了两个看似一样的 if 语句。第一个 if 语句,也就是直接判断“吃过饭没”,在它成立的情况下,代码会打印“吃了”。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Java虚拟机》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(90)

  • 丨落灬小莫
    当替换为2的时候无输出
    当替换为3的时候打印HelloJava及HelloJVM
    猜测是因为将boolean 保存在静态域中,指定了其类型为'Z',当修改为2时取低位最后一位为0,当修改为3时取低位最后一位为1
    则说明boolean的掩码处理是取低位的最后一位

    作者回复: 对的!

    2018-07-23
    3
    184
  • 追梦
    有个地方初看不易看懂,我来解释下:
        作者一开始放的“吃没吃饭”的例子中boolean变量是局部变量,存放在Java方法栈的栈帧中的局部变量区,占据一个数据单元,无需做掩码;最后的例子中boolean变量是成员变量,存储在堆中的对象实例里,占有一个字节,且需要对最后一位做掩码
    2018-10-21
    2
    37
  • 金蝉子
    老师可以讲下ASM、Unsafe和CAS的底层原理吗?这块儿一直是个拦路虎,谢谢!

    作者回复: ASM你指的是那个字节码工程包吗?是的话那就是一个提供了字节码抽象的工具,允许用Java代码来生成或者更改字节码。JDK里也会用到ASM,用来生成一些适配器什么的。我印象中代码覆盖工具JaCoCo也是用ASM来实现的。

    Unsafe就是一些不被虚拟机控制的内存操作的合集。具体想要了解哪个API?

    CAS可以理解为原子性的写操作,这个概念来自于底层CPU指令。Unsafe提供了一些cas的Java接口,在即时编译器中我们会将对这些接口的调用替换成具体的CPU指令。

    2018-07-23
    1
    35
  • 东方
    Unsafe.putBoolean和Unsafe.puByte是native实现

    putBoolean和putByte也是通过宏SET_FIELD模板出的函数

    #define SET_FIELD(obj, offset, type_name, x) \
      oop p = JNIHandles::resolve(obj); \
      *(type_name*)index_oop_from_field_offset_long(p, offset) = truncate_##type_name(x)

    unsafe.cpp中定义宏做truncate
    #define truncate_jboolean(x) ((x) & 1)
    #define truncate_jbyte(x) (x)
    #define truncate_jshort(x) (x)
    #define truncate_jchar(x) (x)
    #define truncate_jint(x) (x)
    #define truncate_jlong(x) (x)
    #define truncate_jfloat(x) (x)
    #define truncate_jdouble(x) (x)

    综上:unsafe.Put*不会对值做修改
    ------------------------------------------------------------------------------------
    getBoolean和getByte也是通过宏GET_FIELD模板出的函数

    #define GET_FIELD(obj, offset, type_name, v) \
      oop p = JNIHandles::resolve(obj); \
      type_name v = *(type_name*)index_oop_from_field_offset_long(p, offset)

    综上,unsafe.Get*不会对值做修改
    ------------------------------------------------------------------------------------
    验证:
    unsafe.putByte(foo, addr, (byte)2); // 设置为: 2
    System.out.println(unsafe.getByte(foo, addr)); // 打印getByte: 2
    System.out.println(unsafe.getBoolean(foo, addr)); // 打印getBoolean: true

    unsafe.putByte(foo, addr, (byte)1); // 设置为: 1
    System.out.println(unsafe.getByte(foo, addr)); // 打印getByte: 1
    System.out.println(unsafe.getBoolean(foo, addr)); // 打印getBoolean: true
    ------------------------------------------------------------------------------------
    疑问:
    if(foo.flag)判断,使用getfield Field flag:"Z",执行逻辑等于:0 != flag
    if(foo.getFlag())判断,使用invokevirtual Method getFlag:"()Z",执行逻辑等于: 0 != ((flag) & 1)

    求大神帮忙解答

    --------------------------
    附getFlag jasm码:
    public Method getFlag:"()Z"
    stack 1 locals 1
    {
    aload_0;
    getfield Field flag:"Z";
    ireturn;
    }



    https://gist.github.com/qudongfang/49635d86882c03e49cff2b0f7d833805

    作者回复: 研究得非常深入!

    Unsafe.putBoolean会做掩码,另外方法返回也会对boolean byte char short进行掩码

    2018-07-23
    1
    29
  • 午夜的汽笛
    这节看的很吃力,对什么掩码,子码,反码,补码都换给大学老师了。
    2018-09-12
    19
  • Kyle
    老师,文中看你说到:
    “也就是说,boolean、byte、char、short 这四种类型,在栈上占用的空间和 int 是一样的,和引用类型也是一样的。因此,在 32 位的 HotSpot 中,这些类型在栈上将占用 4 个字节;而在 64 位的 HotSpot 中,他们将占 8 个字节。”

    但是我记得boolean在内存中占1字节,char占2字节,这里是什么个意思?

    作者回复: 你说的是在堆里的情况。在解释器栈上是不一样的。至于原因吗,主要是变长数组不好控制,所以就选择浪费一些空间,以便访问时直接通过下标来计算地址。

    2018-07-23
    16
  • Geek_dde3ac
    你好,在内存中都是0,那么是如何区别是哪种类型数据的呢?

    作者回复: 内存中是不做区分的。Java程序想要把它解读成什么类型,它就是什么类型。

    2018-07-23
    13
  • 落叶飞逝的恋
    其实那个boolean的true虚拟机里面为1,也就是if(true==吃了没)其实可以替换成if(1==2)这样理解吧

    作者回复: 对的!

    2018-07-24
    10
  • life is short, enjoy mor...
    老师你好,我刚来订阅,所以才开始看。
    有一个疑问,您的原文“因此,在 32 位的 HotSpot 中,这些类型在栈上将占用 4 个字节;而在 64 位的 HotSpot 中,他们将占 8 个字节。”。但是有一句话,java一次编译,到处运行。计算机位数不一样的话,导致一样类型的size不一样,还可以到处运行吗?这里指的到处运行,是不是需要同位啊?比如32位的编译只能在32位的机器上运行,64只能在64的上运行。能互相兼容运行嘛?
    2018-09-14
    2
    9
  • andy
    我替换成2和3,都只能打印出一个Hello Java为什么呢?下面是AsmTools反编译代码
    super public class Foo
            version 52:0
    {

    static Field boolValue:Z;

    public Method "<init>":"()V"
            stack 1 locals 1
    {
                    aload_0;
                    invokespecial Method java/lang/Object."<init>":"()V";
                    return;
    }

    public static Method main:"([Ljava/lang/String;)V"
            stack 2 locals 1
    {
                    iconst_2;
                    putstatic Field boolValue:"Z";
                    getstatic Field boolValue:"Z";
                    ifeq L18;
                    getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
                    ldc String "Hello, Java!";
                    invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
            L18: stack_frame_type same;
                    getstatic Field boolValue:"Z";
                    iconst_1;
                    if_icmpne L33;
                    getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
                    ldc String "Hello, JVM!";
                    invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
            L33: stack_frame_type same;
                    return;
    }

    } // end Class Foo
    2018-09-13
    1
    8
  • dong
    感觉"吃饭了"例子,弄得有点饶了。也有些地方语句的起承转合不是很通顺,个人理解。

    作者回复: 谢谢建议

    2018-07-27
    8
  • Invincible、
    为什么我不能让boolvalue=2或者3……

    作者回复: 因为javac不支持这种操作,它把boolean是用int实现的这种虚拟机的实现细节给隐藏起来了,从而使得在语言层面没有这种会引起歧义的值。

    2018-11-29
    7
  • 杨春鹏
    局部变量中基本数据类型存储在栈中,变量的变量名(引用符号)和变量值(字面量)都存储在栈中。
    局部变量中引用数据类型的引用地址存储在栈中,对象的实例数据存储在堆中,类型数据存储在方法区
    全局变量的基本数据类型和引用数据类型,都存储在堆中。
    不知理解的是否正确
    2018-07-26
    1
    7
  • 别处
    以下两个引至本文内容:
    1、在 Java 虚拟机规范中,boolean 类型则被映射成 int 类型。
    2、在 HotSpot 中,boolean 字段占用一字节,

    问题:一个是int类型,一个是一个字节(32位系统的话就是byte类型),是没讲透还是错误?

    作者回复: 多谢指出。严格来说,前者指的是计算的时候被映射成int类型,后者指的是存储到堆中是一个字节。

    2018-07-24
    4
  • 志远
    NaN 有一个有趣的特性:除了“!= 始终返回 true”之外,所有其他比较结果都会返回 false。这句话好拗口啊,双引号的标点符号有问题吧

    作者回复: 多谢指出!应该是 “!=“

    2018-07-23
    4
  • 迷上尼古丁的味道
    下面这段中0x8000000是不是少写了一个0呢

    前者在 Java 里是 0,后者是符号位为 1、其他位均为 0 的浮点数,在内存中等同于十六进制整数 0x8000000(即 -0.0F 可通过 Float.intBitsToFloat(0x8000000) 求得)。
    2019-02-22
    3
  • Arvin
    当改为2或者3时则出现编译错误是则么回事!!!!
    2018-10-18
    3
  • crystal
    老师,有个问题请教:第一个例子将过饭没改为2,会输出吃过了;第二个例子将boolValue改为2,却不输出信息。两个变量值都是2,为什么输出的结果会不同?
    2018-08-27
    3
  • 梧桐树
    如果64位处理器,long 和double和其它类型一样都是8个字节码在栈中
    2018-07-26
    3
  • 沉淀的梦想
    为什么我替换成2和3,但是现象和第一讲的时候一样呢?都只能打印出一个Hello Java,我反复检查也没发现什么问题,这有可能是什么原因呢?
    2018-07-25
    3
收起评论
90
返回
顶部