【发布时间】:2017-05-08 18:15:38
【问题描述】:
我正在尝试了解 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
}
如您所见,要分配给树节点的值首先存储在局部变量中。查看生成的字节码,结果如下:
将变量从常量池加载到堆栈 // 例如字符串“一些值”
变量在局部变量中的存储
从方法目标加载到堆栈上 // 例如TypeBTreeNode
从局部变量中加载变量 // “Some Value”
setter 的调用
然而,通过不存储到局部变量并直接传递参数,这可以写得更短。所以,它变成了:
将方法目标推入堆栈 // 例如 TypeBTreeNode
然后将常量加载到堆栈中//“某些值”
然后调用setter
我知道在其他语言(例如 C++)中,编译能够进行这种优化。
在 Java 中,HotSpot 优化器在运行时负责这种魔法。
但是,据我了解文档,HotSpot 仅在第 500 次方法调用(客户端 VM)之后才启动。
问题:
我是否理解正确:如果我只初始化每棵树一次,但对大量(比如说 10.000)生成的 TreeInitializer 执行此操作,则每个 TreeInitializer 都会执行第一个字节码序列,因为它们是不同的类使用不同的方法,每个方法只调用一次?
我怀疑不使用本地变量重写类型器会显着加快速度,因为我节省了大约三分之一的字节码指令和可能昂贵的变量负载。我知道如果不测量就很难判断,但是更改生成器代码并非易事,所以您认为值得一试吗?
【问题讨论】:
-
这些方法看起来很简单。为什么不尝试创建一个通用的解决方案?这可能还会节省大量加载类的时间。
-
这个类只是示范性的。在实际应用中,非专业用户使用 DSL 编写应用逻辑。这样,他们可以创建全新的节点类型,为新字段提供不同的设置器。我可能会错过一些东西,但我认为这无法通过通用解决方案实现。
-
嗯,从远处很难说。您不能将用户类型包装在节点中,而不是用户直接定义节点类型吗?
-
所以你是说,用户要定义数千种不同的节点类型?听起来不是很有说服力。除此之外,如果初始化只发生一次,那么你肯定看错了。用户在初始化之后会对这些节点做一些事情,不是吗?而你对“不使用本地人显着加快重写生成器的速度”的怀疑,no,这不会发生......
标签: java performance bytecode jvm-hotspot