为什么 Integer a = 100; 不创建新对象?从编译到运行的全流程拆解

🦊一句话总结

Integer a = 100; 的完整流程
编译期 :Java 编译器将自动装箱转换为 Integer.valueOf(100) 的字节码。
运行期 :JVM 执行字节码,调用 Integer.valueOf → 检查缓存(-128~127)→ 返回缓存对象。
本质:JVM 对高频整数的内存优化,避免重复创建对象。


详细流程拆解

2.1 编译期:语法糖展开

源码

java 复制代码
Integer a = 100; // Java 语法糖

编译器行为

1、识别自动装箱

  • Integer 是包装类型,100int 字面量 → 触发自动装箱(Autoboxing)。
  • 根据 JLS 规范(JLS §5.1.7),必须使用 valueOf 方法。

2、生成字节码

不生成 new Integer(100) ,而是直接生成 Integer.valueOf(100) 的调用。 如果直接通过 IDEA 查看class文件的源码,有可能看到的代码跟源码一样,反编译器(如 IDEA)会将字节码还原为 Integer a = 100;,但真实字节码是 valueOf 调用。

这里我们通过命令去查看字节码指令:

java 复制代码
0: bipush        100          // 将 int 100 压入操作数栈
2: invokestatic  #7           // 调用 Integer.valueOf(int)(#7 是常量池索引)
5: astore_1                   // 将返回的 Integer 对象存入变量 a

优化

  • 若值超出 bipush 范围(>127),改用 sipush 指令。
  • 若表达式是常量(如 Integer a = 100 + 28;),直接折叠为 Integer.valueOf(128)
java 复制代码
// java源码
Integer b = 100 + 28;
System.out.println(b);

// IDEA显示 class文件代码
Integer b = 128;
System.out.println(b);

// 字节码指令
sipush        128 // 2字节指令,压入 int 128(>127)
invokestatic  #7 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
astore_2

2.2 运行期:JVM 执行

1、字节码执行

1、bipush 100 :将 int 值 100 压入操作数栈。

2、invokestatic Integer.valueOf

  • 调用 Integer.valueOf(int) 方法。

  • 方法内部逻辑:

    java 复制代码
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high) {
            return IntegerCache.cache[i + (-IntegerCache.low)]; // 返回缓存对象
        }
        return new Integer(i); // 超出范围则新建对象
    }

3、astore_1 :将返回的 Integer 对象存入局部变量表 slot 1。

2、缓存检查

  • 缓存范围-128 ~ 127(默认上限,可通过 -XX:AutoBoxCacheMax=<size> 调整)。
  • 命中缓存100 在范围内 → 返回 IntegerCache.cache[228](同一对象)。
  • 未命中缓存 :如 Integer a = 128 → 创建新对象。

3、缓存初始化

IntegerCache 在类加载时预初始化:

java 复制代码
static {
  Integer[] cache = new Integer[127 - (-128) + 1]; // 256 个元素
  for (int i = 0; i < cache.length; i++) {
      cache[i] = new Integer(i - 128); // 预创建 -128 ~ 127 的对象
  }
}

2.3 对比其他写法

写法 底层字节码 是否使用缓存
Integer a = 100; bipush 100 + invokestatic valueOf ✅ 是
Integer a = new Integer(100); new Integer + invokespecial <init> ❌ 否
Integer a = Integer.valueOf(100); 显式调用 valueOf(同自动装箱) ✅ 是
Integer a = Integer.parseInt("100"); 返回 int + 自动装箱(同 valueOf ✅ 是

关键验证

3.1 缓存命中验证

java 复制代码
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true(同一对象,来自缓存)

3.2 超出缓存验证

java 复制代码
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false(不同对象)

3.3 字节码验证

bash 复制代码
javap -c Main.class

输出:

java 复制代码
0: bipush        100
2: invokestatic  #7  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1

性能优化与建议

优先用 int 替代 Integer:高频操作(如循环、计算)避免装箱开销。

比较 Integer 值时用 equals== 可能因缓存导致意外结果(如 200 == 200false)。

调整缓存上限 :若业务中大量使用 Integer(如 0 ~ 1000),可通过 -XX:AutoBoxCacheMax=1000 扩展缓存。

相关推荐
星释6 分钟前
Rust 练习册 21:Hello World 与入门基础
开发语言·后端·rust
u***096412 分钟前
后端服务熔断降级策略,错误率阈值 什么是服务熔断降级
java·开发语言
绝无仅有13 分钟前
大厂面试题MySQL解析:MVCC、Redolog、Undolog与Binlog的区别
后端·面试·架构
烤麻辣烫14 分钟前
23种设计模式(新手)-3接口隔离原则
java·开发语言·学习·设计模式·intellij-idea
绝无仅有14 分钟前
MySQL面试题解析:MySQL读写分离与主从同步
后端·面试·架构
MegatronKing15 分钟前
一个有意思的问题引起了我的反思
前端·后端·测试
JohnYan21 分钟前
Bun技术评估 - 30 SSE支持
javascript·后端·bun
程序猿_极客23 分钟前
【2025最新】 Java 入门到实战:数组 + 抽象类 + 接口 + 异常(含案例 + 语法全解析+巩固练习题)
java·开发语言·后端·java基础·java入门到实战
v***431740 分钟前
spring.profiles.active和spring.profiles.include的使用及区别说明
java·后端·spring
IT_陈寒1 小时前
Vue3性能优化实战:5个被低估的Composition API技巧让你的应用快30%
前端·人工智能·后端