我的问题如下:
Java 代码通常会像这样实现泛型集合:
public class GenericCollection<T> {
private Object[] data;
public GenericCollection () {
// Backing array is a plain object array.
this.data = new Object[10];
}
@SuppressWarnings( "unchecked" )
public T get(int index) {
// And we just cast to appropriate type when needed.
return (T) this.data[index];
}
}
例如这样使用:
for (MyObject obj : genericCollection) {
obj.myObjectMethod();
}
由于 genericCollection 的泛型类型被删除,JVM 似乎没有办法知道在 genericCollection 的“数据”数组中真的只有 MyObject 实例,因为数组的实际类型是 Object,所以可能是其中的一个字符串,并且在其上调用“myObjectMethod”会引发异常。
因此,我假设 JVM 必须执行一些运行时检查体操,以了解该 GenericCollection 实例中真正的内容。
现在看看这个实现:
public class GenericCollection<T> {
private T[] data;
@SuppressWarnings( "unchecked" )
public GenericCollection ( Class<T> type ) {
// Create a type specific array.
this.data = (T[]) Array.newInstance( type, 10 );
}
public T get ( int index ) {
// No unsafe casts needed.
return this.data[index];
}
}
在这种情况下,我们通过反射创建了一个类型特定的数组,因此 JVM 可以推断在给定的上下文中该数组中只能有 T 个对象,从而使不安全的转换和可能的昂贵类型检查变得多余。
我的问题是,考虑到 HotSpot 可以做的事情,它是否会以任何方式在性能方面帮助实现具有“适当”类型特定支持数组的通用集合?
例如,它是否有助于 HotSpot 删除不必要的类型检查或强制转换?如果它知道支持数组是特定类型,也许可以让它更容易地内联方法?
最佳答案
不是在这种特殊情况下。
通用数组 T[]
在字节码中被删除为 Object[]
。 Object[]
的数组 getter 始终返回 Object
,因此不需要检查数组的实际类型。因此,使用 T[]
而不是 Object[]
进行数组获取操作没有任何好处。在这两种情况下都有 aaload
指令后跟 checkcast
,并且其工作方式相同。
与此同时,数组 setter 对于类型化数组比 Object[]
表现更差,因为 aastore
必须检查值是否与实际数组组件类型匹配。
也就是说,您建议的修改对于 get
的效果相同,但对于 set
的效果差。这可以通过以下 JMH 基准得到证实。
package bench;
import org.openjdk.jmh.annotations.*;
import java.lang.reflect.Array;
@State(Scope.Benchmark)
public class Generics {
private ObjectArray<String> objectArray;
private GenericArray<String> genericArray;
private StringArray stringArray;
private int index;
@Param("100000")
private int length;
@Setup
public void setup() {
genericArray = new GenericArray<>(String.class, length);
objectArray = new ObjectArray<>(length);
stringArray = new StringArray(length);
for (int i = 0; i < length; i++) {
String s = Integer.toString(i);
objectArray.set(i, s);
genericArray.set(i, s);
stringArray.set(i, s);
}
}
@Benchmark
public String getGenericArray() {
return genericArray.get(nextIndex());
}
@Benchmark
public String getObjectArray() {
return objectArray.get(nextIndex());
}
@Benchmark
public String getStringArray() {
return stringArray.get(nextIndex());
}
@Benchmark
public void setGenericArray() {
genericArray.set(nextIndex(), "value");
}
@Benchmark
public void setObjectArray() {
objectArray.set(nextIndex(), "value");
}
@Benchmark
public void setStringArray() {
stringArray.set(nextIndex(), "value");
}
private int nextIndex() {
if (++index == length) index = 0;
return index;
}
static class GenericArray<T> {
private T[] data;
@SuppressWarnings("unchecked")
public GenericArray(Class<T> type, int length) {
this.data = (T[]) Array.newInstance(type, length);
}
public T get(int index) {
return data[index];
}
public void set(int index, T value) {
data[index] = value;
}
}
static class ObjectArray<T> {
private Object[] data;
public ObjectArray(int length) {
this.data = new Object[length];
}
@SuppressWarnings("unchecked")
public T get(int index) {
return (T) data[index];
}
public void set(int index, T value) {
data[index] = value;
}
}
static class StringArray {
private String[] data;
public StringArray(int length) {
this.data = new String[length];
}
public String get(int index) {
return data[index];
}
public void set(int index, String value) {
data[index] = value;
}
}
}
结果:
Benchmark (length) Mode Cnt Score Error Units
Generics.getGenericArray 100000 avgt 40 5,212 ± 0,038 ns/op <- equal
Generics.getObjectArray 100000 avgt 40 5,224 ± 0,043 ns/op <-
Generics.getStringArray 100000 avgt 40 4,557 ± 0,051 ns/op
Generics.setGenericArray 100000 avgt 40 3,299 ± 0,032 ns/op <- worse
Generics.setObjectArray 100000 avgt 40 2,456 ± 0,007 ns/op <-
Generics.setStringArray 100000 avgt 40 2,138 ± 0,008 ns/op
关于java - 类型化数组是否有助于 JIT 更好地优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36253866/