Java性能:true vs. Boolean.TRUE

36

以下哪种方法在性能和内存使用效率方面更好?

Boolean isItTrue(arg){ 
    return Boolean.TRUE;
}

boolean isItTrue(arg){
    return Boolean.TRUE
}

Boolean isItTrue(arg){
    return true;
}

boolean isItTrue(arg){
    return true;
}

使用基本类型应该更快、更容易,但另一方面,当使用静态对象的引用时,不会创建新值。或者在编译器级别上进行优化,将所有的 truefalse 替换为对静态对象的引用以节省内存?


4
可能是微小的优化。在什么情况下被称为这样? - Mitch Wheat
可能会对这个讨论串有所帮助。 - trailmax
9个回答

33
首先,使用其中任何一种相比其他方式的性能优势很可能太小而不相关。在绝大多数情况下,代码的简洁性、可读性和可维护性更为重要。
引用块: 这里存在一个基本误解。布尔类型是一种原始类型(而非引用类型)。true和false值无法被替换为引用。此外,用于保存true和false的空间取决于上下文。通常是一个字节,远小于在64位JVM上存储引用所需的8个字节(如果oops压缩被禁用)。因此,即使在技术上可能,这种“优化”也不会节省内存。
没有你的例子涉及到创建Boolean实例2,所以我们可以在性能分析中排除它。
# Example 1
boolean isItTrue(arg){
    return true;
}

这个方法将会与其他所有方法一样快甚至更快,因为它只需要将一个寄存器设为零即可。
# Example 2
Boolean isItTrue(arg){ 
    return Boolean.TRUE;
}

从孤立的角度来看,这需要从内存中加载一个静态引用,而不是将寄存器清零。然而,在某些情况下,即时编译器可能能够对此进行优化。
# Example 3
Boolean isItTrue(arg){
    return true;
}

表面上,这涉及到调用Boolean.valueOf(true)来"装箱"这个true,但是JIT编译器应该能够通过内联调用来优化它,使其与之前的代码相同。
# Example 4
boolean isItTrue(arg){
    return Boolean.TRUE
}

表面上看,这涉及到调用Boolean.booleanValue(Boolean.TRUE)来“拆箱”Boolean。这个调用可以内联。同时,即时编译器也有可能避免加载对Boolean对象的引用并获取其值字段。
总结一下,你的四个例子的相对性能取决于JIT编译器在优化方面的成功程度。这将取决于上下文、JIT编译器的具体设置等等。理论上,JIT编译器可以为所有例子生成相同(最优)的代码。实际上,在几乎所有情况下,差异都是微不足道的。(而且这本身也不是一个实际的例子。)

1 - 这个基准测试无意中证明了这一点。如果它所呈现的结果是可信的,那么最好和最差版本之间的性能差异每次调用不到1纳秒。
2 - 理论上有可能其中4个触发了Boolean类的初始化,并且你的应用程序本来不会这样做。在这种极不可能的情况下,你的整个应用程序将分配2个本来不会被分配的对象。初始化可能需要几微秒,并且在长期内消耗几个字节的RAM(少于50字节)。这在实践中是无关紧要的。


13

如果有任何性能提升,那么它是微不足道的,可以忽略不计。 Boolean.TRUE和Boolean.FALSE在任何情况下都不返回新对象。


12
优先考虑代码可维护性,而非微小的优化。问题不应是“哪个更小/更快”,而应该先关注如何表达你的意思。
如果方法返回一个布尔对象,那么接收者需要决定是否存在可能为null的情况,如果它是null,则可能表示true/false以外的其他含义,例如“我们不知道”。
因此,如果你的意思是布尔类型,就返回布尔类型,否则,如果你想允许Null,就返回Boolean类型。
如果要返回布尔值,则...
return true; // or false

为了提高清晰度和性能,必须优于依赖自动装箱。

如果返回的是布尔值,则

return Boolean.TRUE

必须要好,因为它避免了创建额外的垃圾。尽管我反对微小优化,但我认为故意低效是没有价值的。我会认为这样做也更清晰,因为你显然在匹配返回类型。


7

它们将更快。我认为这两者之间没有任何区别。

Boolean isItTrue(arg){ 
    return Boolean.TRUE;
}

boolean isItTrue(arg){
    return true;
}

但是其他的实现会比较慢,因为它会在后台进行装箱和拆箱操作,从而占用处理器的一些时间。


编辑

我通过实现4种不同的方法收集了一些事实。只是想与你分享,我不知道这是否是正确的做法。

Boolean isItTrue(){ 
    return Boolean.TRUE;
}

Free Memory before start --> 16030936
Time taken in Secs --> 7.844
Free Memory After Process --> 15940472
Memory Usage --> 90464

boolean isItTrue(){
    return Boolean.TRUE;
}

Free Memory before start --> 16030936
Time taken in Secs --> 10.109
Free Memory After Process --> 15940472
Memory Usage --> 90464

Boolean isItTrue(){
    return true;
}

Free Memory before start --> 16030936
Time taken in Secs --> 7.906
Free Memory After Process --> 15940472
Memory Usage --> 90464

boolean isItTrue(){
    return true;
}

Free Memory before start --> 16030936
Time taken in Secs --> 7.828
Free Memory After Process --> 15940472
Memory Usage --> 90464

Main Class

public static void main(String[] args){
    NewClass n = new NewClass();

    long sysTime = System.currentTimeMillis();

    Runtime rt = Runtime.getRuntime();
    long freeMem = rt.freeMemory();
    System.out.println( "Free Memory before start --> " + freeMem );
    for( int i = 0; i < Integer.MAX_VALUE; i++ ){
        n.isItTrue();
    }
    System.out.println( "Time taken in Secs --> " + (System.currentTimeMillis() - sysTime)/1000D);
    System.out.println( "Free Memory After Process --> " + rt.freeMemory() );
    System.out.println( "Memory Usage --> " + ( freeMem - rt.freeMemory() ) );
}

1
我认为你声称“它们会快得多”是不正确的。任何性能差异都将是微不足道的。 - Paul Cager
1
嘿,我在实现后添加了一些事实。看一下。 - Talha Ahmed Khan
4
微基准测试通常很难编写,因此通常建议对实际应用程序进行分析。请查看 http://www.ibm.com/developerworks/java/library/j-jtp02225/index.html 和 http://wikis.sun.com/display/HotSpotInternals/MicroBenchmarks。 - Paul Cager
3
这项基准测试存在缺陷,可能会使结果无效。 即使数字有效,它们也不能代表该方法的使用方式,因此也不能代表JIT编译器优化它们的环境。 最后,2 ^ 31次调用的最佳-最差时间差为2.3秒,相当于不到一纳秒,在标准硬件上只需2或3个CPU时钟周期。 如果这是代表性的话,那显然对典型应用程序的性能影响很小。 - Stephen C

4
最后一个。
boolean isItTrue(arg){
    return true;
}

我只在方法需要有时返回null时才使用Boolean


3

我的经验法则如下:

  1. 默认选择是原始类型(boolean)。
  2. 如果需要可为空性或需要将值存储在容器中,则使用类(Boolean)。

考虑到这一点,我的默认选择是:

boolean isItTrue(arg){
    return true;
}

就性能而言,唯一确定的是很难想象在使用boolean时会比使用Boolean更快。它是否更慢或相同取决于许多因素,并且通常无法回答。
如果您真的关心这个问题,请对代码进行分析以找到瓶颈所在!

2

按照这种逻辑,静态对象本身的引用和真值一样昂贵,甚至更昂贵。

使用对象可能比原始类型稍慢,但我不会担心:差异是无关紧要的。


2
使用最后一个(只是boolean)。即使编译器将它们全部优化为相同的东西,至少你让编译器的工作更容易了(你知道这不是一项容易的工作!)。
此外,这样做可以减少按键次数(不需要按shift键)。但实际上,你应该使用包装类的唯一原因是当你需要将其设置为null时,以及在通用数据结构(如LinkedList<E>)中使用。

0

java.lang.Boolean 占用 16 字节。

如果您只关注性能和内存大小问题,那么这是最佳选择:

boolean isItTrue(arg){
    return true;
}

3
如果你的程序导致初始化了Boolean类,那么 Boolean.TRUEBoolean.FALSE对象仍然会被创建,所以你不会节省任何空间。 - Stephen C
它会在后台初始化布尔值吗?我原本以为不会。 - Marcus Granström
不在后台运行。但JVM规范说明,如果使用任何静态内容、调用任何静态方法或创建任何对象实例(和其他内容),则将发生初始化。如果发生任何这些情况,将创建TRUEFALSE实例并使用空间……无论您的应用程序是否使用这些实例。 - Stephen C
Boolean.TRUE是一个静态字段,被所有自动装箱为Booleantrue重复使用。 - Steve Kuo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接