Type Specialization的paper,这里也已经有翻译了。简单的说,就是出于性能考虑,避免基础类型的装箱拆箱,为基础类型保留了特定的实现。
class V[@specialized T]{
var i:T = _
}
上面用了specialized
注释,除了定义一个泛型的V[A]
,之外,编译器还会额外生成9个特定的类型:
% scalac V.scala && ll
-rw-r--r-- 1 hongjiang wheel 1060 11 5 16:07 V$mcB$sp.class
-rw-r--r-- 1 hongjiang wheel 1070 11 5 16:07 V$mcC$sp.class
-rw-r--r-- 1 hongjiang wheel 1066 11 5 16:07 V$mcD$sp.class
-rw-r--r-- 1 hongjiang wheel 1063 11 5 16:07 V$mcF$sp.class
-rw-r--r-- 1 hongjiang wheel 1065 11 5 16:07 V$mcI$sp.class
-rw-r--r-- 1 hongjiang wheel 1060 11 5 16:07 V$mcJ$sp.class
-rw-r--r-- 1 hongjiang wheel 1063 11 5 16:07 V$mcS$sp.class
-rw-r--r-- 1 hongjiang wheel 1032 11 5 16:07 V$mcV$sp.class
-rw-r--r-- 1 hongjiang wheel 1063 11 5 16:07 V$mcZ$sp.class
-rw-r--r-- 1 hongjiang wheel 3549 11 5 16:07 V.class
-rw-r--r-- 1 hongjiang wheel 41 11 5 16:07 V.scala
hongjiang@whj-mbp /tmp/dd % javap V\$mcD\$sp
Compiled from "V.scala"
public class V$mcD$sp extends V{
public double i$mcD$sp;
public double i$mcD$sp();
public double i(); // 这个是针对double类型的
public void i$mcD$sp_$eq(double);
public void i_$eq(double);
public boolean specInstance$();
public void i_$eq(java.lang.Object);
public java.lang.Object i();
public V$mcD$sp();
}
从上面看到,哪些匿名类的类名中的:B,C,D,F,I,J,S,V,Z,分别对应 byte,char,double,float,int,long,short,void(scala里的Unit), boolean
在类型参数比较多的情况下,specialization会产生类爆炸的情况,参考stackoverflow上的一个例子:
class Huge[@specialized A, @specialized B, @specialized C](
val a: A, val b: B, val c: C
) {} // 730 files, 2.9 MB
上面的类产生了730个文件:9x9x9+1。之前在模拟perm gen的oom时,用到过这个特性。
//2012.5.30
在REPL下,模拟perm gen的oom案例:
虽然perm gen确实在不断增加,并且可能会OOM(如果perm gen设置比较小的话),但通过jconsole的类加载来看并非把这些定义的class都load了;对比了前后的class load
之前:3797 个class被load
运行一次:
class Test[@specialized A, @specialized B, @specialized C, @specialized D]
之后:4180 ,增加了383个而不是 9x9x9x9=6561个 why?
通过jvisualvm工具dump了前后的内存对比,scala.tools.nsc.io.VirtualFile
实例数从2增加到了 6573,增长了 6571 个,并且每运行一次都会增加 6571个,比6561多出10个
继续测试
class Test[@specialized A]
也是会使 VirtualFile 新增 19个而非9个,另外多出来的10个实例还没有清楚是什么。