Как распознать бокс / распаковку в декомпилированном коде Scala? - PullRequest
8 голосов
/ 13 февраля 2011

В принятом лучшем ответе на на этот вопрос есть четкое объяснение, почему происходит бокс.

Однако, если я декомпилирую код (используя декомпилятор Java), я не вижу использования scala.runtime.BoxesRunTime. Кроме того, если я профилирую код (используя JProfiler), я не вижу никаких экземпляров BoxesRunTime.

Итак, как я могу увидеть доказательство того, что бокс / распаковка происходит?

Ответы [ 2 ]

7 голосов
/ 13 февраля 2011

В этом коде:

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 выглядит так, как будто это объект.

Вы, вероятно, должны искать примитивы в штучной упаковке (например, java.lang.Integer) сJProfile, хотя я не уверен, как работает JVM и может ли он на самом деле переписать код во время выполнения и оптимизировать его, чтобы избежать упаковки.Насколько мне известно, это не должно применять специализацию (но я считаю, что CLR делает).Несколько микробенчмарков с ситуацией бокса и без нее - еще один способ выяснить, что происходит во время выполнения.

РЕДАКТИРОВАТЬ:

Выше предполагается, что параметр типа не был аннотирован с @specialized аннотация.В этом случае бокс / распаковка можно избежать.Некоторые классы в стандартной библиотеке являются специализированными.Смотрите этот sid .

1 голос
/ 13 февраля 2011

Учитывая следующую программу 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();
      ()
    }
  }
}

Может быть, поэтому вы не видите никаких доказательств бокса в байт-коде ...

...