【问题标题】:Does casting null to an array of objects cost more than casting to an object?将 null 转换为对象数组是否比转换为对象的成本更高?
【发布时间】:2017-02-09 22:53:55
【问题描述】:

我有以下课程

public void dump(Integer value){
    //do soemthing with value
}
public void dump(Integer[] values){
    //do soemthing with values
}

我想打电话给dump(null),我选择转换为什么并不重要,因为两者都可以按预期使用空值:

    dump((Integer[]) null);
    dump((Integer) null);
  1. 哪个更好地使用资源(更少的 ram、cpu 使用)?
  2. 我认为 java 会为 null Integer 预分配 4 个字节,为 Double 预分配 8 个字节,这是真的吗?
  3. 如果没有构造函数调用,那么其他(更复杂的)为空的对象如何存储空值?

【问题讨论】:

  • 两者都不会在呼叫站点使用更多资源。哪个将使用更少的 cpu 取决于这两个函数在收到时如何处理 null 值。 null 未分配,因此 (Integer)null(Double)null 使用完全相同的堆空间量......零。
  • 我认为您的问题大部分由stackoverflow.com/questions/2430655/…回答

标签: java


【解决方案1】:
  1. 哪个更好地使用资源(更少的 ram、cpu 使用)?

它不会产生任何可衡量的差异。

  1. 我认为 java 会为 null Integer 预分配 4 个字节,为 Double 预分配 8 个字节,这是真的吗?

没有。空为空。只有一种格式。您在这里混淆了对象和引用。 null 是一个参考。所有引用的大小都相同。

其他(更复杂的)被清空的对象呢

不存在“无效的对象”之类的东西。

如果没有构造函数调用,那么空值如何存储?

作为空值。

【讨论】:

  • 被清空的对象,我的意思是像“TextView textView = null”这样的类,而不是简单的数字
  • @ThenEnok 你再一次混淆了对象和引用。 textView 是一个参考。
  • 我明白你的意思,我应该说“其他(更复杂的)对象被赋予了 null 的变量呢?”
  • 叹息。对象不拥有变量。引用变量指向对象,或者为空。将引用变量设置为 null 不会对它之前可能指向的任何对象执行任何操作。
  • 我现在明白了,谢谢,如果不使用 Integer 我会使用上面的 texview 之类的东西,是否会有更大的差异,我只是好奇。
【解决方案2】:

强制转换的类型不影响 null 使用的内存。在反编译的类中:

aconst_null
checkcast     #4                  // class java/lang/Integer
invokevirtual #5                  // Method dump:(Ljava/lang/Integer;)V

aconst_null
checkcast     #6                  // class "[Ljava/lang/Integer;"
invokevirtual #7                  // Method dump:([Ljava/lang/Integer;)

所以两者在内存使用方面是相同的。

【讨论】:

  • checkcast 演员表,它在运行时执行。它就在字节码中。
【解决方案3】:

我相信它们的成本是相等的,因为在这两种情况下您都没有实际的对象(Integer 对象和Integer[] 对象都没有,它们在内存中的大小肯定不同)。

您在这里所拥有的只是 4 字节的引用,它指向任何内容,因为它为空。从这个角度来看,您将这些null-s 投射到的类型之间没有区别。

【讨论】:

    【解决方案4】:
    1. 没有区别。
    2. 不。 Java 根本不为null 对象分配任何空间,因为null 根据定义是对任何对象的引用。没有任何物体根本不占用空间。引用本身总是 32 或 64 位长。
    3. “存储”是什么意思?引用变量保存一个指针值,无论它指向的对象如何,它的大小始终相同。无论对象的大小如何,即使引用指向非null 值,无论它引用什么类型,引用大小始终相同。它是地址的大小。我没看过,但我敢打赌,null 引用是一个特殊的地址值,例如 0,根据定义,它没有指向任何地方。

    强制转换引用不会更改引用。它仍然具有与没有强制转换时完全相同的值,一点一点。

    唯一的例外是针对 64 位 Java 的 32 位优化。通常,您希望 64 位 Java 中的所有引用都是 64 位长。但是,如果有关程序的某些假设成立,您可以打开或关闭以 32 位保存引用的能力。但无论哪种方式,一旦 JVM 决定了引用的宽度是 32 位还是 64 位,这将适用于整个程序。

    所以最重要的是,不,将引用转换为其他类型对引用消耗的内存没有影响。

    如果对它的所有引用都超出范围或变为null,则该对象消耗的内存可能会归零。

    【讨论】:

      【解决方案5】:

      所有的引用值(以及装箱的 Integer 和数组都是引用值)具有相同的表示形式,因此调用完全没有区别。如果成本有差异,则取决于调用的函数的实现。

      【讨论】:

        【解决方案6】:
        1. 哪个更好地使用资源(更少的 ram、cpu 使用)?

        如果您需要问自己这个问题,您可以随时编写一个简单的测试。

        public class Main {
            private static Integer x = 0;
            private static final int n = 500000000;
        
            public static void dump(Integer value){
                x += 1;
            }
            public static void dump(Integer[] values){
                x += 1;
            }
        
            public static void main(String[] args) {
                long t = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    dump((Integer[]) null);
                }
                long array_time = System.currentTimeMillis() - t;
                t = System.currentTimeMillis();
                for (int i = 0; i < n; i++) {
                    dump((Integer) null);
                }
                long int_time = System.currentTimeMillis() - t;
                System.out.println("array_time: " + array_time + " ms");
                System.out.println("int_time: " + int_time + " ms");
            }
        }
        

        输出:

        array_time: 2578 ms
        int_time: 2045 ms
        

        差别不大。

        【讨论】:

        • 更好的测试是:“javap -c Main”来比较字节码
        • 基准测试无效。您未能预热 JVM。您还对dump() 进行了相当复杂的操作,这既不必要又降低了结果的重要性。请参阅另一个答案中提供的字节码。时代没有任何理由略有不同。
        猜你喜欢
        • 2019-07-11
        • 1970-01-01
        • 1970-01-01
        • 2020-06-15
        • 2016-03-11
        相关资源
        最近更新 更多