现代 JVM 是否优化只读集合分配
Does a modern JVM optimize read only collection allocations
我对下面的优化可能性有点好奇,但不确定如何自己验证,也许有人已经知道答案了。
我们的应用程序中有很多类似的代码。基本上它是一个有状态的处理程序 (saga),由一些消息 (MyStartingMessage
) 启动,稍后可以由其他消息 (MyCotinueMessage
) 继续。因为这些 StatefullHandler
有多个实例,所以我们要检查 Continue 消息是否应由给定实例处理。因此,我们在处理程序中设置了一些状态,当收到消息时,我们检查每个处理程序实例是否消息中的状态与处理程序状态匹配,只有这样消息才由该处理程序实例处理。
在下面显示的示例中,某些框架将提取匹配器并检查是否有任何匹配器与接收到的继续消息实例匹配。
因为我们每秒处理数千条这样的消息,所以我很好奇 JVM 是否确实优化了实例分配(一段时间后)而不是每次都创建一个新实例。
class StatefullHandler implements MessageHandler {
public void handle(final MyStartingMessage m) {
m.setState(m.getUserId()); // used by the matchers
}
public void handle(final MyContinueMessage m) {
// handle
}
// only returned for queries and never modified
public Collection<MessageMatcher> matchers() {
// message will only be handled by this instance if state matches
// is this new operator optimized 'away' after some time??
return ImmutableSet.of(MessageMatcher.for(MyContinueMessage.class, msg -> msg.getUserId()));
}
}
如果没有优化,我实际上需要这样写:
class StatefullHandler implements MessageHandler {
private static final MATCHERS = ImmutableSet.of(
MessageMatcher.for(MyContinueMessage.class, msg -> msg.getUserId()));
public void handle(final MyStartingMessage m) {
m.setState(m.getUserId()); // used by the matchers
}
public void handle(final MyContinueMessage m) {
// handle
}
public Collection<MessageMatcher> matchers() {
return MATCHERS;
}
}
如果您明确使用 new
关键字,JVM 将无法重用旧对象实例,因为这会违反 Java 语言规范。首先,当您通过 new
创建对象 A
和 B
时,可以保证 A == B
将 return false
。其次,保证您可以在 A
和 B
上独立同步,而无需相互等待。如果 JVM 重用旧对象,则无法保证这一点。因此 JVM 不能重用它们,即使是最简单的情况,如 new Integer(1)
.
在某些情况下,您可以依赖第三方库执行的缓存,但在您的特定情况下,Guava 的 ImmutableSet.of
仅对空集重用现有对象。因此,在您的情况下,static
字段似乎是最好的解决方案。
虽然 JVM 不能重用一个对象,但它可以避免首先创建它。
如果在内联之后(即查看您的调用者和它的调用者等),您的对象以相同的方法生死存亡,则可以将其放置在堆栈中,并可能使用转义分析将其消除。
在您的情况下,完全有可能不反对 of()
但 lambda 对象也被消除,这不会创建任何对象。这完全取决于您的用例以及优化器是否可以消除对象创建。
我建议您使用商业分析器或飞行记录器来检查是否实际创建了对象。
我对下面的优化可能性有点好奇,但不确定如何自己验证,也许有人已经知道答案了。
我们的应用程序中有很多类似的代码。基本上它是一个有状态的处理程序 (saga),由一些消息 (MyStartingMessage
) 启动,稍后可以由其他消息 (MyCotinueMessage
) 继续。因为这些 StatefullHandler
有多个实例,所以我们要检查 Continue 消息是否应由给定实例处理。因此,我们在处理程序中设置了一些状态,当收到消息时,我们检查每个处理程序实例是否消息中的状态与处理程序状态匹配,只有这样消息才由该处理程序实例处理。
在下面显示的示例中,某些框架将提取匹配器并检查是否有任何匹配器与接收到的继续消息实例匹配。
因为我们每秒处理数千条这样的消息,所以我很好奇 JVM 是否确实优化了实例分配(一段时间后)而不是每次都创建一个新实例。
class StatefullHandler implements MessageHandler {
public void handle(final MyStartingMessage m) {
m.setState(m.getUserId()); // used by the matchers
}
public void handle(final MyContinueMessage m) {
// handle
}
// only returned for queries and never modified
public Collection<MessageMatcher> matchers() {
// message will only be handled by this instance if state matches
// is this new operator optimized 'away' after some time??
return ImmutableSet.of(MessageMatcher.for(MyContinueMessage.class, msg -> msg.getUserId()));
}
}
如果没有优化,我实际上需要这样写:
class StatefullHandler implements MessageHandler {
private static final MATCHERS = ImmutableSet.of(
MessageMatcher.for(MyContinueMessage.class, msg -> msg.getUserId()));
public void handle(final MyStartingMessage m) {
m.setState(m.getUserId()); // used by the matchers
}
public void handle(final MyContinueMessage m) {
// handle
}
public Collection<MessageMatcher> matchers() {
return MATCHERS;
}
}
如果您明确使用 new
关键字,JVM 将无法重用旧对象实例,因为这会违反 Java 语言规范。首先,当您通过 new
创建对象 A
和 B
时,可以保证 A == B
将 return false
。其次,保证您可以在 A
和 B
上独立同步,而无需相互等待。如果 JVM 重用旧对象,则无法保证这一点。因此 JVM 不能重用它们,即使是最简单的情况,如 new Integer(1)
.
在某些情况下,您可以依赖第三方库执行的缓存,但在您的特定情况下,Guava 的 ImmutableSet.of
仅对空集重用现有对象。因此,在您的情况下,static
字段似乎是最好的解决方案。
虽然 JVM 不能重用一个对象,但它可以避免首先创建它。
如果在内联之后(即查看您的调用者和它的调用者等),您的对象以相同的方法生死存亡,则可以将其放置在堆栈中,并可能使用转义分析将其消除。
在您的情况下,完全有可能不反对 of()
但 lambda 对象也被消除,这不会创建任何对象。这完全取决于您的用例以及优化器是否可以消除对象创建。
我建议您使用商业分析器或飞行记录器来检查是否实际创建了对象。