【JVM】Int类型在栈中是否会被缓存?

简介: 【JVM】Int类型在栈中是否会被缓存?

在写面试题系列文章中,多次涉及到JVM的内存分布情况,以及方法执行的过程中局部变量的存储变化情况。比如,在此前已经讲解过字符串常量池的初始化及使用情况。

前些天一位粉丝加微信好友,询问关于int类型的一张存储结构图,主要是对int类型在方法执行的过程中是否存在缓存的情况有疑问。在交流、探讨的过程中收获很多相关知识。本篇文章就汇总分享一下。

int类型的是否会被缓存

首先看下图(其他公众号文章获得),图中显示int类型在栈中会被复用。image.png针对引用类型我们知道栈中只存储引用地址,而对应的值存储在堆中,这没什么问题。而针对int(等基础)类型,变量和值都是存储在栈中(其实也不一定,后面会讲到),那么int类型是否会像字符串常量一样,指向同一个值呢?

用代码展示:

int a = 18;
int b = 18;

变量a和b的值是否是同一个18呢?这便是粉丝提出的疑问,针对此疑问进行了一路探究,逐步获得不少相关知识,下面来逐步讲解。

int类型的入栈指令

要看JVM的操作,查看class文件是必不可少的。而针对int类型,有iconst、bipush、sipush、ldc入栈指令。

当int取值在-1~5时,JVM采用iconst指令将常量压入栈中。如:

int i = 5;
int j = -1;

对应的JVM指令:

Code:
    0: iconst_5
    1: istore_1
    2: iconst_m1
    3: istore_2

int类型的0~5对应JVM的入栈指令分别为iconst_0、iconst_1、iconst_2、iconst_3、iconst_4、iconst_5。当int类型为-1时对应指令为iconst_m1。

当int取值-128~127(一个字节)时,入栈指令均为bipush。

Code:
    0: bipush        127

当int取值-32768~32767(两个字节)时,入栈指令为sipush。

Code:
    0: sipush        32767

当int取值-2147483648~2147483647(三到四个字节)时,入栈指令为ldc。我们知道ldc指令是从常量池进行加载,也就是说当超过2个字节时,int类型会被存储在常量池中。这就是前面说的,为什么int类型不一定都存储在栈中。

经过这一步的分析,我们得知了int类型在JVM操作指令层面的区别,同时也得知即便是int类型,也不一定是变量和值都存储在栈中。

关于int类型在编译器被存储到常量池可以定义一个比较大的int,然后查看class文件便会看到,这里不再举例。

局部变量与操作数栈

在了解局部变量和操作数栈之前,我们先来了解一下栈帧的结构,如下图:image.png栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。方法的调用过程其实就是入栈和出栈的过程。

其实在编译代码时,栈帧中需要多大的局部变量表,多深的操作数栈都已经确定了,并且写入到了方法表的Code属性中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体虚拟机的实现。

下面看一下class文件的内容,我们便能看到上图中对应的Local Variable Table。image.png上面是通过javap -verbose命令查看class文件中对应的信息。我们可以看到执行的命令部分在Code中,也可以看到局部变量表位于LocalVariableTable部分。

在上图中局部变量表中可以看到Slot一列,其中分别为0、1、2、3。局部变量表的容量以变量槽(Slot)为最小单位。

Slot可以存放boolean、byte、char、short、int、float、reference和returnAddress 8种类型。其中reference 表示对一个对象实例的引用,通过它可以得到对象在Java 堆中存放的起始地址的索引和该数据所属数据类型在方法区的类型信息。

在结构图中,局部变量表下面便是操作栈。那么局部变量表和操作栈是如何协作来完成相应的功能的呢?我们先来看一段相加的代码。

public class IntTest {
  public void test1(){
    int a = 100;
    int b = 98;
    int c = a + b;
  }
}

对应的局部变量表和操作栈的操作如下图:image.png两个int值100和98依次被加载到局部变量表中,计算时先将100、98入栈存放到操作栈中,然后从栈顶依次取出对应的值进行计算,最后再将计算结果存入局部变量表中。

通过javap -verbose命令查看字节码我们再来对照一下上述流程。image.png对照上图中操作字节码的编号和对应局部变量表的变化,能够更加清晰的理解上述过程。

其中局部变量表的第0个Slot明显看到是this当前对象。后面的Slot依次存放了a、b、c三个变量。

总结一下

通过上述的分析,我们基本可以确定针对int类型JVM操作时并不一定会进行缓存处理,只有当int值大小超过2个字节时才会进入常量池。

而最开始粉丝质疑的那张图也的确有问题。虽然没办法直接证明,但只用将98和100换成相同的值,会发现bipush了两次,同时局部变量表中也存了两个变量,从侧面可以证明图的错误。image.png看完本篇文章如果你收获了知识点那么恭喜你,但如果同时发现交流会促进更多思考,挖掘更多新知识,那更应该祝贺你了。


目录
相关文章
|
8月前
|
Web App开发 存储 缓存
如何精准清除特定类型或标签的缓存数据?
如何精准清除特定类型或标签的缓存数据?
664 57
|
12月前
|
Python Windows
[oeasy]python076_int这个词怎么来的_[词根溯源]整数类型_int_integer_touch
本文探讨了“int”一词的起源及其与整数类型的关联。通过词根溯源,揭示“int”来源于“integer”,意为“完整的数”,与零碎的分数相对。同时分析了相关词汇如“tact”(接触)、“touch”(触摸)及衍生词,如“tangential”(切线的)、“intagible”(无形的)和“integral”(完整的、不可或缺的)。文章还结合编程语言特性,解释了Python作为动态类型、强类型语言的特点,并总结了整型变量的概念与意义。最后预告了后续内容,提供了学习资源链接。
427 11
|
12月前
|
存储 C语言 Python
[oeasy]python077_int类型怎么用_整数运算_integer_进制转化_int类
本文主要讲解了Python中`int`类型的应用与特性。首先回顾了`int`词根的溯源,探讨了整型变量的概念及命名规则(如匈牙利命名法)。接着分析了整型变量在内存中的存储位置和地址,并通过`type()`和`id()`函数验证其类型和地址。还介绍了整型变量的运算功能,以及如何通过`int()`函数将字符串转化为整数,支持不同进制间的转换(如二进制转十进制)。此外,文章提及了关键字`del`的使用场景,对比了Python与C语言中`int`的区别,并总结了整型与字符串类型的差异,为后续深入学习奠定基础。
301 1
|
缓存 安全 算法
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
230 0
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
234 3
【Java基础面试五】、 int类型的数据范围是多少?
这篇文章回答了Java中`int`类型数据的范围是-2^31到2^31-1,并提供了其他基本数据类型的内存占用和数值范围信息。
【Java基础面试五】、 int类型的数据范围是多少?
|
Python
[oeasy]python036_数据类型有什么用_type_类型_int_str_查看帮助
本文回顾了Python中`ord()`和`chr()`函数的使用方法,强调了这两个函数互为逆运算:`ord()`通过字符找到对应的序号,`chr()`则通过序号找到对应的字符。文章详细解释了函数参数类型的重要性,即`ord()`需要字符串类型参数,而`chr()`需要整数类型参数。若参数类型错误,则会引发`TypeError`。此外,还介绍了如何使用`type()`函数查询参数类型,并通过示例展示了如何正确使用`ord()`和`chr()`进行转换。最后,强调了在函数调用时正确传递参数类型的重要性。
170 3
|
自然语言处理 Go 数据安全/隐私保护
对 int 类型的数据加密,有哪些好的方案?
对 int 类型的数据加密,有哪些好的方案?
255 13
|
缓存 NoSQL Redis
一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)
这篇文章是关于Java面试中可能会遇到的五个问题,包括MySQL索引类型及其对数据库性能的影响、Redis的RDB和AOF持久化机制、Redis的过期键删除策略、Redis的单线程模型为何高效,以及缓存雪崩、缓存穿透和缓存击穿的概念及其解决方案。
|
Arthas 存储 监控
JVM内存问题之JNI内存泄漏没有关联的异常类型吗
JVM内存问题之JNI内存泄漏没有关联的异常类型吗
183 1