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

Ner*_*vic 8 boxing unboxing scala

在对这个问题的公认最佳回答中,有一个明确的解释为什么拳击发生.

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

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

axe*_*l22 7

在这段代码中:

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


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

bar应首先调用整数.使用Scala 2.8.1进行编译并使用:

javap -c -l -private -verbose -classpath <dir> Main$
Run Code Online (Sandbox Code Playgroud)

看到为类的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                                                                                     
...
Run Code Online (Sandbox Code Playgroud)

您可以在通话BoxesRunTime前看到通话bar.

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

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

编辑:

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