我正在尝试了解JVM和HotSpot优化器内部。
我解决了尽可能快地使用大量节点初始化对象树结构的问题。 现在,对于给定的每个树结构,我们生成Java源代码以初始化树,如下所示。最后,我们有数千个这样的课程。
public class TypeATreeNodeInitializer {
public TypeATreeNode initialize(){
return getTypeATree();
}
private TypeATreeNode getTypeATree() {
TypeATreeNode node = StaticTypeAFactory.create();
TypeBTreeNode child1 = getTypeBTreeNode1();
node.getChildren().add(child1);
TypeBTreeNode child2 = getTypeBTreeNode2();
node.getChildren().add(child2);
//... may be many more children
return node;
}
private TypeBTreeNode getTypeBTreeNode1() {
TypeBTreeNode node = StaticTypeBFactory.create();
TypeBTreeNode child1 = getTypeCTreeNode1();
node.getChildren().add(child1);
//store of value in variable first
String value1 = "Some value";
// assign value to node
node.setSomeValue(value1);
boolean value2 = false;
node.setSomeBooleanValue(value2);
return node;
}
private TypeBTreeNode getTypeCTreeNode1() {
// ...
return null;
}
private TypeBTreeNode getTypeBTreeNode2() {
// ...
return null;
}
//... many more child node getter / initializer
}
如您所见,要分配给树节点的值首先存储在局部变量中。查看生成的字节代码,结果如下:
从常量池到堆栈的变量加载//例如字符串“Some Value”
变量存储在局部变量
从方法目标到堆栈的负载//例如TypeBTreeNode
从局部变量加载变量//“Some Value”
调用setter
然而,这可以通过不存储到局部变量并直接传递参数来缩短。所以,它变成了:
将方法目标推送到堆栈上//例如TypeBTreeNode
然后将常量加载到堆栈//“Some Value”
然后调用setter
我知道在其他语言(例如C ++)中,编译能够进行这样的优化。
在Java中,HotSpot优化器在运行时负责这种魔术 但是,据我所知,HotSpot只在500ths方法调用(客户端VM)之后启动。
问题:
我是否理解正确:如果我只对每个树初始化一次,但是对于生成的TreeInitializers的大量数据(假设为10.000),则为每个TreeInitializer执行第一个字节代码序列,因为它们是不同的类使用不同的方法,每个方法只调用一次?
我怀疑使用没有本地人的重写速度很快,因为我节省了大约三分之一的字节码指令和可能的昂贵的变量。我知道没有测量就很难说,但改变发生器代码是非常重要的,所以你认为值得一试吗?
答案 0 :(得分:1)
删除像这样的临时/堆栈变量几乎总是过早优化。您的处理器每秒可处理数亿条指令;同时,如果您正在初始化成千上万的任何,那么您的程序可能会在等待内存分配时阻塞。
我的建议始终是推迟优化,直到您对代码进行了分析。在此期间,编写代码尽可能易于阅读,这样当您需要返回并修改某些内容时,很容易找到需要更新的地方。