如何在反编译的Scala代码中识别装箱/拆箱?

时间:2011-02-13 05:04:58

标签: scala boxing unboxing

在对this question的公认最佳回应中,有一个明确的解释为什么拳击发生。

但是,如果我反编译代码(使用java反编译器),我看不到使用scala.runtime.BoxesRunTime。此外,如果我分析代码(使用JProfiler),我看不到BoxesRunTime的任何实例。

那么,我怎么才能看到拳击/拆箱的证据呢?

2 个答案:

答案 0 :(得分:7)

在此代码中:

class Foo[T] {
  def bar(i: T) = i
}


object Main {
  def main(args: Array[String]) {
    val f = new Foo[Int]
    f.bar(5)
  }
}

bar的调用应该首先输入整数。使用Scala 2.8.1进行编译并使用:

javap -c -l -private -verbose -classpath <dir> Main$

查看为main类的Main方法生成的字节码产生:

public void main(java.lang.String[]);                                                      
...                                                                                   
   9:   iconst_5                                                                                          
   10:  invokestatic    #24; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;      
   13:  invokevirtual   #28; //Method Foo.bar:(Ljava/lang/Object;)Ljava/lang/Object;        
   16:  pop                                                                                            
   17:  return                                                                                     
...

在致电BoxesRunTime之前,您可以看到bar的来电。

BoxesRunTime是一个包含原始类型的装箱方法的对象,因此总共应该只有一个实例。这里的技巧是库中的这个特定文件是用Java编写的,转换是静态方法。出于这个原因,在运行时没有它的任何实例,尽管在Scala代码中使用它感觉就好像它是一个对象。

您可能应该使用JProfile查找盒装基元(例如java.lang.Integer),但我不确定JVM是如何工作的,以及它是否可以在运行时实际重写代码并优化它以避免装箱。据我所知,它不应该应用专业化(但我相信CLR确实如此)。一些有和没有装箱情况的微基准测试是另一种弄清楚运行时会发生什么的方法。

编辑:

以上假设类型参数未使用@specialized注释进行注释。在这种情况下,可以避免装箱/拆箱。标准库中的某些类是专用的。请参阅this sid

答案 1 :(得分:1)

鉴于以下Test.scala程序:

object Test {
  def main(args:Array[String]) {
    val list = List(1,5,15)
    val res = list.map(e => e*2).filter(e => e>10)
  }
}

如果我使用scalac -Xprint:jvm Test.scala进行编译,我会得到这个片段,表明发生了专门化(抱歉是广泛粘贴):

package <empty> {
  final class Test extends java.lang.Object with ScalaObject {
    def main(args: Array[java.lang.String]): Unit = {
      val list: List = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 5, 15}));
      val res: List = list.map({
        (new Test$$anonfun$1(): Function1)
      }, immutable.this.List.canBuildFrom()).$asInstanceOf[scala.collection.TraversableLike]().filter({
        (new Test$$anonfun$2(): Function1)
      }).$asInstanceOf[List]();
      ()
    };
    def this(): object Test = {
      Test.super.this();
      ()
    }
  };
  @SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp {
    final def apply(e: Int): Int = Test$$anonfun$1.this.apply$mcII$sp(e);
    <specialized> def apply$mcII$sp(v1: Int): Int = v1.*(2);
    final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Int.box(Test$$anonfun$1.this.apply(scala.Int.unbox(v1)));
    def this(): Test$$anonfun$1 = {
      Test$$anonfun$1.super.this();
      ()
    }
  };
  @SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$2 extends scala.runtime.AbstractFunction1$mcZI$sp {
    final def apply(e: Int): Boolean = Test$$anonfun$2.this.apply$mcZI$sp(e);
    <specialized> def apply$mcZI$sp(v1: Int): Boolean = v1.>(10);
    final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Boolean.box(Test$$anonfun$2.this.apply(scala.Int.unbox(v1)));
    def this(): Test$$anonfun$2 = {
      Test$$anonfun$2.super.this();
      ()
    }
  }
}

可能是你没有在字节码中看到拳击的任何证据的原因......