有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java扩展AnyVal和AnyRef的对象的内存分配有什么不同

因此,在Java中,我们看到基本类型比对象类型占用更少的内存空间。例如intInteger的实例占用更少的空间。 出于这个原因,通常更明智的做法是char而不是String存储单个字符

我想了解这是如何转化为Scala的。 没有原语。我们所拥有的只是AnyValAnyRef的实例。 每个内存分配之间是否存在差异


共 (3) 个答案

  1. # 1 楼答案

    Scala中,Scala在编译时使用box/unboxBoxesRunTime.java处理原语类型:

    • 框表示将原语类型解析为引用类型

    字节码

    def foo[T](v: T) = v.toString
    def bar(i: Int)= foo(i)
    :javap -c bar
    public java.lang.String bar(int);
      Code:
         0: getstatic     #19                 // Field .MODULE$:L;
         3: iload_1
         4: invokestatic  #25                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
         7: invokevirtual #29                 // Method .foo:(Ljava/lang/Object;)Ljava/lang/String;
        10: areturn
    

    在上述bar方法中的字节码中,它将框原语int-type转换为int-reference-type。这是因为我们将原语类型int用于泛型T

    • unbox表示将引用类型解析为基元类型

    字节码:

     def foo(i: Int) = i
        def bar[T](i: T) = foo(i.asInstanceOf[Int])
        public <T> int bar(T);
         Code:
            0: getstatic     #19                 // Field .MODULE$:L;
            3: aload_1
            4: invokestatic  #24                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
            7: invokevirtual #28                 // Method .foo:(I)I
    

    在上面的bar方法字节码中,我们正在将一个引用类型解析为基元Int类型,因此需要将其解装箱为基元Int类型

    因此,在运行时中,始终存在用于类型解析的box或unbox。它可能会导致性能下降并消耗更多内存。但我们可以通过以下方式避免:

    它们都用于解决scala中的box/unbox问题

  2. # 2 楼答案

    Is there are difference between the memory allocations for each

    AnyRef与插入object类似,在插入object时,由于头的原因,每个对象的x86/x64的正常开销分别为8/16字节

    Scala中的AnyVal允许优化一个非常特定的用例,其中您有一个val参数,它被包装在class中,并且您可以在运行时将所述类表示为基础值,而不是分配类的实例

    AnyVal有几个限制:

    1. Must have only a primary constructor with exactly one public, val parameter whose type is not a value class. (From Scala 2.11.0, the parameter may be non-public.)
    2. May not have specialized type parameters.
    3. May not have nested or local classes, traits, or objects
    4. May not define a equals or hashCode method.
    5. Must be a top-level class or a member of a statically accessible object
    6. Can only have defs as members. In particular, it cannot have lazy vals, vars, or vals as members.
    7. Cannot be extended by another class.

    例如,如果我们以from the documentation为例:

    class Wrapper(val underlying: Int) extends AnyVal {
      def foo: Wrapper = new Wrapper(underlying * 19)
    }
    

    Wrapper在编译时的表示仍然是一个类。但是在运行时,底层表示将是一个Int,而不是一个带有额外Int字段的类Wrapper

    但并不总是保证Wrapper在运行时会成为Int表示。当:

    1. 值类被视为另一种类型
    2. 将值类指定给数组
    3. 执行运行时类型测试,例如模式匹配
  3. # 3 楼答案

    当然,实际上有原语。Scala只是(有意)试图隐藏差异

    实际上有4个案例,而不是2个:

    1. 扩展AnyRef的类。它们只是普通的Java风格的类,具有相同的内存使用

    2. Unit。它对应于Java void,因此通常根本不在运行时表示。如果是,则使用BoxedUnit

    3. IntBoolean等等。虽然它们看起来像Scala代码中的类,但它们通常表示为JVM原语。但在某些情况下,它们不能被使用,而是使用装箱类型(Integer,等等)。也就是说,当它们是泛型类型/方法的参数时,除了Array

    4. 用户定义的值类。见尤瓦尔·伊扎科夫的答案。当然,如果有一个值类包装一个原语,那么它最终会被表示为一个原语本身。请注意,它们在运行时并不总是表示为底层类型,特别是对于Array[ValueClass],与案例3相反