一、核心概念

1.1 什么是 Long 缓存机制?

Java 中的 Long 类(以及其他部分包装类如 IntegerShortByteCharacter)为了提升性能和减少内存开销,内部维护了一个静态缓存池(称为 LongCache),用于缓存一定范围内的常用 Long 对象。

  • 目的:避免频繁创建相同值的包装对象,复用已有对象,减少 GC 压力。
  • 缓存范围-128127(包含两端),共 256 个 Long 对象。
  • 触发方式:通过 Long.valueOf(long) 方法访问,而非 new Long(...)
  • 实现原理Long 类内部有一个静态数组 LongCache.cache,在类加载时初始化,存储 -128127Long 实例。

⚠️ 注意:Long 缓存是 JVM 规范要求 的(Java SE 规范),所有兼容 JVM 必须实现此缓存。


二、操作步骤(非常详细)

步骤 1:理解缓存的创建时机

  1. JVM 启动,加载 java.lang.Long 类。
  2. 执行 static 块,初始化 private static class LongCache
  3. 创建一个 Long 数组 cache[],长度为 256
  4. 循环填充数组:
    • cache[0] 存储 new Long(-128)
    • cache[1] 存储 new Long(-127)
    • ...
    • cache[128] 存储 new Long(0)
    • ...
    • cache[255] 存储 new Long(127)
  5. 缓存创建完成,后续可通过 valueOf() 复用。

步骤 2:使用 valueOf() 获取缓存对象(推荐方式)

// ✅ 正确使用缓存
Long a = Long.valueOf(100);  // 值在 [-128,127] 范围内 → 返回缓存对象
Long b = Long.valueOf(100);  // 再次获取 → 返回**同一个**缓存对象

System.out.println(a == b);  // 输出: true(引用相等)
System.out.println(a.equals(b)); // true(值相等)

步骤 3:对比 new Long()(绕过缓存,不推荐)

// ❌ 绕过缓存,每次创建新对象
Long c = new Long(100);
Long d = new Long(100);

System.out.println(c == d);  // 输出: false(引用不同)
System.out.println(c.equals(d)); // true(值相等)

步骤 4:测试超出缓存范围的值

Long e = Long.valueOf(200);  // 超出范围 → 创建新对象
Long f = Long.valueOf(200);  // 再次调用 → 再次创建新对象

System.out.println(e == f);  // 输出: false(非缓存对象,不重用)

步骤 5:自动装箱也使用缓存

// ✅ 自动装箱语法糖等价于 valueOf()
Long g = 100L;      // 编译后 → Long.valueOf(100)
Long h = 100L;      // 编译后 → Long.valueOf(100)

System.out.println(g == h);  // true(使用缓存)

但:

Long i = 200L;      // Long.valueOf(200)
Long j = 200L;      // Long.valueOf(200)

System.out.println(i == j);  // false(超出缓存,新对象)

三、常见错误

错误 1:使用 == 比较 Long 对象

Long x = 300L;
Long y = 300L;
if (x == y) { ... } // ❌ 可能为 false!应使用 equals()

✅ 正确做法:

if (x.equals(y)) { ... } // ✅ 正确比较值

错误 2:认为所有小数值都缓存

Long a = Long.valueOf(127);
Long b = Long.valueOf(127);
System.out.println(a == b); // true

Long c = Long.valueOf(128); // 刚好超出
Long d = Long.valueOf(128);
System.out.println(c == d); // false!

错误 3:在性能敏感场景频繁创建大 Long

// ❌ 每次都创建新对象,GC 压力大
for (int i = 0; i < 1000000; i++) {
    Long num = new Long(i + 1000); // 大量临时对象
    process(num);
}

四、注意事项

  1. 缓存范围固定-128127不可配置(与 Integer 不同,Integer 的缓存上限可通过 -XX:AutoBoxCacheMax 调整,但 Long 不支持)。
  2. valueOf() 有效new Long() 永远不走缓存。
  3. 自动装箱走缓存Long x = 100L; 等价于 Long.valueOf(100)
  4. 线程安全:缓存对象是 不可变的Long 是 final 类,值不可变),因此缓存是线程安全的。
  5. 内存占用:缓存 256 个 Long 对象,内存开销极小(约几 KB),但换来巨大性能收益。
  6. 只适用于常用值:若应用频繁使用 1000, 2000 等值,缓存无效,需考虑其他优化。

五、使用技巧

  1. 优先使用 valueOf() 或自动装箱

    Long num = 100L; // ✅ 推荐
    // 而非 new Long(100)
    
  2. 比较 Long 使用 equals()

    if (a != null && a.equals(b)) { ... }
    
  3. 利用缓存优化高频小数值

    • 如状态码、标志位、小 ID 等使用 0-127 范围内的值,可享受缓存优势。
  4. 避免在循环中创建大 Long

    // ❌ 避免
    for (long i = 1000; i < 1000000; i++) {
        Long wrapper = i; // 自动装箱 → 每次可能创建新对象
    }
    
    // ✅ 优化:使用基本类型
    for (long i = 1000; i < 1000000; i++) {
        processPrimitive(i); // 传 long,避免装箱
    }
    

六、最佳实践

推荐做法

  1. 统一使用自动装箱或 valueOf()

    Long id = 42L;                    // ✅ 清晰、使用缓存
    Long count = Long.valueOf(n);     // ✅ 显式调用
    
  2. 值比较用 equals()

    if (value != null && value.equals(100L)) { ... }
    
  3. 性能敏感场景减少装箱

    • 在循环、高频调用中,优先使用 long 基本类型。
    • 使用 Long.parseLong() 获取 long,而非 Long.valueOf(str) 获取对象。
  4. 理解缓存边界

    • 明确 127 是缓存上限,128 不缓存。

七、性能优化

场景 推荐方式 性能优势
获取小数值 Long 对象 Long.valueOf(100)100L 复用缓存,零创建开销
获取大数值 Long 对象 Long.valueOf(1000) 仍优于 new Long()(语义一致)
高频数值计算 使用 long 基本类型 避免装箱/拆箱开销
字符串转数值 Long.parseLong(str) 返回 long,无对象创建

🔍 性能对比(小数值)

  • new Long(100):每次分配对象 → 慢,GC 压力大
  • Long.valueOf(100):返回缓存引用 → 极快(O(1) 查表)

八、总结

Long 缓存机制是 JVM 为优化性能而内置的重要特性,核心在于 复用 -128127 范围内的 Long 对象

✅ 核心要点速查:

项目 说明
缓存范围 -128127(含)
触发方法 Long.valueOf(long) 和自动装箱
不触发 new Long(...)
比较方式 引用用 ==(仅限缓存内小值),值比较用 equals()
性能优势 减少对象创建,降低 GC,提升速度
适用类型 Byte, Short, Integer, Long, Character 都有类似缓存

🚀 实践口诀:

小数装箱用缓存,== 比较要小心;大数频繁要规避,基本类型性能高。