Domanda

Nella migliore risposta accettata a questa domanda, c'è una chiara spiegazione del perché si verifica la boxe.

Tuttavia, se decompilo il codice (usando il decompiler Java) non riesco a vedere l'uso di scala.runtime.BoxesRunTime.Inoltre, se profiliamo il codice (utilizzando JProfiler) non riesco a vedere alcuna istanza di BoxesRunTime.

Quindi, come posso vedere realmente una prova dell'avvenuto inscatolamento/unboxing?

È stato utile?

Soluzione

In questo codice:

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


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

L'invocazione di bar dovrebbe prima racchiudere il numero intero.Compilando con Scala 2.8.1 e utilizzando:

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

per vedere il bytecode prodotto per il main metodo del Main rendimento della classe:

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                                                                                     
...

Puoi vedere la chiamata a BoxesRunTime prima della chiamata a bar.

BoxesRunTime è un oggetto che contiene metodi di boxing per i tipi primitivi, quindi dovrebbe esserci esattamente un'istanza in totale.Il problema qui è che questo particolare file nella libreria è stato scritto in Java e le conversioni sono metodi statici.Per questo motivo non ce ne sono istanze in fase di runtime, anche se usarlo nel codice Scala sembra come se fosse un oggetto.

Probabilmente dovresti cercare primitive boxed (ad es.java.lang.Integer) con JProfile, anche se non sono sicuro di come funzioni la JVM e se possa effettivamente riscrivere il codice in fase di esecuzione e ottimizzarlo per evitare il boxing.Per quanto ne so, non dovrebbe applicare la specializzazione (ma credo che CLR lo faccia).Alcuni microbenchmark con e senza la situazione del boxing sono un altro modo per capire cosa succede in fase di esecuzione.

MODIFICARE:

Quanto sopra presuppone che un parametro di tipo non sia stato annotato con il file @specialized annotazione.In questo caso il boxing/unboxing può essere evitato.Alcune classi nella libreria standard sono specializzate.Vedere questo sid.

Altri suggerimenti

Dato il seguente programma 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)
  }
}

Se compilo con scalac -Xprint:jvm Test.scala, Ottengo questo frammento che suggerisce che si verifica la specializzazione (scusate per la pasta larga):

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();
      ()
    }
  }
}

Potrebbe essere il motivo per cui non vedi alcuna prova della boxe in bytecode ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top