性能文章>Java:100==100为true,1000==1000也可以为true!>

Java:100==100为true,1000==1000也可以为true!原创

1年前
6940216

现象

我们先运行一段代码,代码片段如下:

Integer a = 1000, b = 1000;
System.out.println(a == b);//1
Integer c = 100, d = 100;
System.out.println(c == d);//2

如果基础比较好的同学,一眼就能看出来,这段代码结果为false和true,即两个值为1000的Integer不想等,但是两个值为100的Integer却是想等的。

原作者这里分享了一段基础常识:如果两个引用指向同一个对象,那么==判断是为true,如果指向不同对象,那么==判断就是false,即使它们的内容是完全一样的。

但是,如果按照这个基础常识来判断的话,上面那段代码中c==d也应该是false才对,说明JDK源代码肯定在这里动了一些手脚,我们接着往下看!

原因

如果你看过Integer.java的源码就会知道,它里面有一个内部私有类IntegerCache.java,它缓存了所有[-128,127]之间的Integer对象:

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        int i = parseInt(integerCacheHighPropValue);
        // 最大值不允许小于127
        i = Math.max(i, 127);
        // 最大值不允许超过 Integer.MAX_VALUE
        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++){
            cache[k] = new Integer(j++);
        }

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

而我们平常申明一个Integer变量的代码:Integer i = 100,它背后的代码实际上是````Integer i = Integer.valueOf(100)```。因为Java有自动装箱处理这个语法糖,所以我们简单申明即可。而Integer.valueOf()源码如下,它优先从IntegerCache中获取,获取不到的情况下才会new一个Integer。所以,只要是cache中的Integer对象比较,==的结果都会是true:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

为什么

现在可能你会想问,为什么需要cache?因为,根据统计在这个范围内的小整数要比这个范围外的大整数使用频率高很多。所以,cache很有必要,它能显著减少内存开销。

有趣的代码

接下来,原作者还给了一段很有意思的代码,源码如下:

public static void main(String[] args) throws Exception {
    Class cache = Integer.class.getDeclaredClasses()[0]; //1
    Field myCache = cache.getDeclaredField("cache"); //2
    myCache.setAccessible(true);//3
    // 通过反射获取到Integer中cache了的对象集合
    Integer[] newCache = (Integer[]) myCache.get(cache); //4
    // 因为默认从-128缓存到127,所以下标为133对应的就是5,132对应的是4
    newCache[132] = newCache[133]; //5
    int a = 2;
    int b = a + a;
    System.out.printf("%d + %d = %d", a, a, b); 
}

代码解读:b的结果本来应该是4,这个很容易验证。最后执行System.out.println(b);即可。但是System.out.printf("%d + %d = %d", a, a, b); 的结果却是2 + 2 = 5,这是因为前一个println方法可以接受int类型参数,意味着它不用自动装箱。而后一个printf方法接受的参数是Object类型,意味着int类型变量需要自动装箱为Integer,而本来的4即newCache[132]已经被反射的代码改成newCache[133]即5,所以我们看到的是2 + 2 = 5。

魔法

最后,笔者再来加点小魔法,让两个值为1000的Integer变量==为true,甚至只要你愿意,让两个任意值的Integer变量都可以==为true。如何控制呢?非常简单,我们看一下IntegerCache.java这个类的注释就明白了:

* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
* The cache is initialized on first usage.  The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the sun.misc.VM class.

通过这段注释,我们只需要配置JVM参数:-XX:AutoBoxCacheMax=1000,就能让两个值为1000的Integer对象==为true了,一个非常简单但是非常神奇的JVM参数,你get到了嘛?

分类:
标签:
请先登录,再评论

面试这是送命题呀

11年前

👍,get 了

1年前

为你推荐

字符串字面量长度是有限制的
前言 偶然在一次单元测试中写了一个非常长的字符串字面量。 正文 在一次单元测试中,我写了一个很长的字符串字面量,大概10万个字符左右,编译时,编译器给出了异常告警 `java: constant
多次字符串相加一定要用StringBuilder而不用-吗?
今天在写一个读取Java class File并进行分析的Demo时,偶然发现了下面这个场景(基于oracle jdk 1.8.0_144): ``` package test; public c
如何通过反射获得方法的真实参数名(以及扩展研究)
前段时间,在做一个小的工程时,遇到了需要通过反射获得方法真实参数名的场景,在这里我遇到了一些小小的问题,后来在部门老大的指导下,我解决了这个问题。通过解决这个问题,附带着我了解到了很多新的知识,我觉得
高吞吐、低延迟 Java 应用的 GC 优化实践
本篇原文作者是 LinkedIn 的 Swapnil Ghike,这篇文章讲述了 LinkedIn 的 Feed 产品的 GC 优化过程,虽然文章写作于 April 8, 2014,但其中的很多内容和
「每日五分钟,玩转 JVM」:久识你名,初居我心
聊聊 JVMJVM,一个熟悉又陌生的名词,从认识Java的第一天起,我们就会听到这个名字,在参加工作的前一两年,面试的时候还会经常被问到JDK,JRE,JVM这三者的区别。JVM可以说和我们是老朋友了
据说99.99%的人都会答错的类加载的问题
概述首先还是把问题抛给大家,这个问题也是我厂同学在做一个性能分析产品的时候碰到的一个问题。 同一个类加载器对象是否可以加载同一个类文件多次并且得到多个Class对象而都可以被java层使用吗请仔细注意
Java多线程——并发测试
编写并发程序时候,可以采取和串行程序相同的编程方式。唯一的难点在于,并发程序存在不确定性,这种不确定性会令程序出错的地方远比串行程序多,出现的方式也没有固定规则。那么如何在测试中,尽可能的暴露出这些问
Java多线程知识小抄集(一)
本文主要整理笔者遇到的Java多线程的相关知识点,适合速记,故命名为“小抄集”。本文没有特别重点,每一项针对一个多线程知识做一个概要性总结,也有一些会带一点例子,习题方便理解和记忆。 1.interr