深入理解JVM2 -- Java的基本类型

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

前言

我们都知道Java存在基本数据类型和引用数据类型,这里就会有个疑问 既然是面向对象,为什么不直接都设计为引用类型?

这是因为为了性能和内存的友好性而设计出的基本数据类型,而这里所谓的性能和内存友好也就是偏底层,基本数据类型更加贴近底层数据结构,而不用封装箱,所以性能更好。

那本篇内容就来说说Java的基本类型。

正文

Java一共有8种基本数据类型,我们先来看看boolean类型在JVM中是如何处理的。

JVM的boolean类型

在Java语言规范中,boolean类型的值只有2种可能,分别用符号"true"和"false"来表示,而这2个符号显然不能被JVM直接使用,因为在虚拟机或者底层层面,肯定使用一个标志位更好,而不是使用字符串符号来做比对,所以在Java虚拟机规范中,boolean类型被映射为int类型。

具体来说也就是true被映射为整数1,而false被映射为整数0,这可以大大简化虚拟机的工作。比如对于存储boolean数组的字节码,JVM需要保证其实际的值是1或者0即可。

JVM规范也要求Java编译器遵守这个编码规则,并且使用整数相关的字节码来实现逻辑运行以及基于boolean类型的条件跳转。所以在编译成的class文件中,除了字段和传入参数外,基本看不出boolean类型的痕迹。

Java的基本类型

除了boolean类型外,Java的基本类型还包括整数类型byte、short、char、int和long,以及浮点类型float和double。

如下图:

image.png

我们可以发现基本类型,都有它自己的值域以及默认值,而byte、short、int、long、float以及double的值域依次扩大,所以前面的基本类型转换至后面的基本类型,无需强制转换。

Java基本类型的大小

这里你或许有点疑惑,前面我们说了boolean类型被映射为int类型,而int类型则是由4个字节保存的,那这是不是有歧义呢 所以Java基本类型的大小在JVM中是如何保存的我们来梳理一下。

栈帧

在上一篇文章中我们说过,JVM每调用一个Java方法,便会创建一个栈帧,这里只讨论供解释器使用的解释栈帧。这种栈帧由2个主要组成部分,分别是局部变量区以及字节码的操作数栈。

注意这里的局部变量是广义的,除了包含参数、局部变量外,还包含实例方法的"this"指针,但是在Java虚拟机规范中,局部变量区等价于一个数组,并且可以由正整数来索引,除了long、double值需要用2个数组单元来存储,其他基本类型以及引用类型的值均暂用一个数组单元。

这啥意思呢 比如我定义了一个方法,在这个方法内有局部变量boolean、byte等等,但是存储在栈上占用的控件是和int一样的,即boolean、byte、char、short、int以及引用类型在32位的HotSpot中这些类型都是占用4个字节。

但是这种情况仅仅存在于局部变量,而不会出现在存储于堆中的字段或者数组元素上,对于byte、char和short这3种类型的字段或者数组单元,他们在堆上占用的空间分别是1、2、3个字节,和这些类型的值域吻合。

加载

前面既然说了基本类型在栈中和堆中存储是不一样的,那当加载也就不一样。Java虚拟机的算数运算几乎全部依赖操作数栈,也就是说,我们需要将堆中boolean、byte、char以及short加载到操作数栈上,然后将栈上的值当成int类型运算。

其实也非常简单,对于boolean、char这2个无符号类型来说,加载也就把值放到低2字节,高二字节填0;对于byte和short类型来说,加载伴随这符号扩展,比如short的大小位2个字节,在加载时同样把值复制到int类型的低2字节,如果short为非负数,即最高位为0,就把该int类型值的高2位字节用0来填充。

说到这,其实也就说完了基本类型是在JVM中是如何保存的了。

疑惑解答

本篇内容其实有些内容不太好理解,很多知识在后面会继续解答。

  • 为什么在解释栈中基本类型的存储和堆不一样呢 原因是变长数组不好控制,所以JVM选择浪费一些空间,一边访问时直接通过下标来计算地址。

  • 在方法的局部变量中,对于基本数据类型来说,变量的变量名(引用符号)和变量值都存储在栈中;在方法的局部变量中,对于引用类型来说,对象的实例数据存储在堆中,引用地址存储在栈中,类型数据存储在方法区中;

总结

本章内容主要解释了基本类型,下一篇开始真正解释Java类的相关知识。

猜你喜欢

转载自juejin.im/post/7082022405924716558
今日推荐