gpt4 book ai didi

java - jit会优化新对象吗

转载 作者:搜寻专家 更新时间:2023-11-01 01:32:18 25 4
gpt4 key购买 nike

我创建这个类是为了不可变并且有一个流畅的 API:

public final class Message {
public final String email;
public final String escalationEmail;
public final String assignee;
public final String conversationId;
public final String subject;
public final String userId;

public Message(String email, String escalationEmail, String assignee, String conversationId, String subject, String userId) {
this.email = email;
this.escalationEmail = escalationEmail;
this.assignee = assignee;
this.conversationId = conversationId;
this.subject = subject;
this.userId = userId;
}

public Message() {
email = "";
escalationEmail = "";
assignee = "";
conversationId = "";
subject = "";
userId = "";
}

public Message email(String e) { return new Message(e, escalationEmail, assignee, conversationId, subject, userId); }
public Message escalationEmail(String e) { return new Message(email, e, assignee, conversationId, subject, userId); }
public Message assignee(String a) { return new Message(email, escalationEmail, a, conversationId, subject, userId); }
public Message conversationId(String c) { return new Message(email, escalationEmail, assignee, c, subject, userId); }
public Message subject(String s) { return new Message(email, escalationEmail, assignee, conversationId, s, userId); }
public Message userId(String u) { return new Message(email, escalationEmail, assignee, conversationId, subject, u); }

}

我的问题是,当像这样创建新对象时,优化器是否能够避免大量对象创建:

Message m = new Message()
.email("foo@bar.com")
.assignee("bar@bax.com")
.subject("subj");

创建一个单独的可变构建器对象有什么好处吗?

更新 2:阅读 apangin 的回答后,我的基准无效。我会把它放在这里以供引用如何不进行基准测试:)

更新:我冒昧地用这段代码自己测量了这一点:

public final class Message {
public final String email;
public final String escalationEmail;
public final String assignee;
public final String conversationId;
public final String subject;
public final String userId;

public static final class MessageBuilder {
private String email;
private String escalationEmail;
private String assignee;
private String conversationId;
private String subject;
private String userId;

MessageBuilder email(String e) { email = e; return this; }
MessageBuilder escalationEmail(String e) { escalationEmail = e; return this; }
MessageBuilder assignee(String e) { assignee = e; return this; }
MessageBuilder conversationId(String e) { conversationId = e; return this; }
MessageBuilder subject(String e) { subject = e; return this; }
MessageBuilder userId(String e) { userId = e; return this; }

public Message create() {
return new Message(email, escalationEmail, assignee, conversationId, subject, userId);
}

}

public static MessageBuilder createNew() {
return new MessageBuilder();
}

public Message(String email, String escalationEmail, String assignee, String conversationId, String subject, String userId) {
this.email = email;
this.escalationEmail = escalationEmail;
this.assignee = assignee;
this.conversationId = conversationId;
this.subject = subject;
this.userId = userId;
}

public Message() {
email = "";
escalationEmail = "";
assignee = "";
conversationId = "";
subject = "";
userId = "";
}

public Message email(String e) { return new Message(e, escalationEmail, assignee, conversationId, subject, userId); }
public Message escalationEmail(String e) { return new Message(email, e, assignee, conversationId, subject, userId); }
public Message assignee(String a) { return new Message(email, escalationEmail, a, conversationId, subject, userId); }
public Message conversationId(String c) { return new Message(email, escalationEmail, assignee, c, subject, userId); }
public Message subject(String s) { return new Message(email, escalationEmail, assignee, conversationId, s, userId); }
public Message userId(String u) { return new Message(email, escalationEmail, assignee, conversationId, subject, u); }


static String getString() {
return new String("hello");
// return "hello";
}

public static void main(String[] args) {
int n = 1000000000;

long before1 = System.nanoTime();

for (int i = 0; i < n; ++i) {
Message m = new Message()
.email(getString())
.assignee(getString())
.conversationId(getString())
.escalationEmail(getString())
.subject(getString())
.userId(getString());
}

long after1 = System.nanoTime();

long before2 = System.nanoTime();

for (int i = 0; i < n; ++i) {
Message m = Message.createNew()
.email(getString())
.assignee(getString())
.conversationId(getString())
.escalationEmail(getString())
.subject(getString())
.userId(getString())
.create();
}

long after2 = System.nanoTime();



System.out.println("no builder : " + (after1 - before1)/1000000000.0);
System.out.println("with builder: " + (after2 - before2)/1000000000.0);
}


}

如果字符串参数不是新对象,但我发现差异很明显(构建器更快),但都是一样的(参见 getString 中的注释代码)

在我想象的更现实的场景中,当所有字符串都是新对象时,差异可以忽略不计,并且 JVM 启动会导致第一个稍微慢一点(我尝试了两种方式)。

有了“新字符串”,代码也慢了好几倍(我不得不减少 n),这可能表明正在对“新消息”进行一些优化,但不是“新字符串”。

最佳答案

是的,HotSpot JIT 可以消除本地上下文中的冗余分配。

此优化由 Escape Analysis 提供自 JDK 6u23 起启用。它经常与栈上分配相混淆,但实际上它更强大,因为它不仅允许在栈上分配对象,而且通过用进一步分配的变量(标量替换)替换对象字段来完全消除分配优化。

优化由 -XX:+EliminateAllocations JVM 选项控制,默认情况下打开。


由于分配消除优化,创建 Message 对象的两个示例都以相同的方式有效工作。他们不分配中间对象;只是最后一个。

您的基准测试显示误导性结果,因为它收集了许多 common pitfalls微基准测试:

  • 它将多个基准合并到一个方法中;
  • 它测量一个OSR stub而不是最终的编译版本;
  • 它不进行预热迭代;
  • 它不消耗结果等。

让我们用 JMH 正确测量它.作为奖励,JMH 具有分配分析器 (-prof gc),它显示每次迭代实际分配了多少字节。我添加了第三个测试,该测试在禁用 EliminateAllocations 优化的情况下运行以显示差异。

package bench;

import org.openjdk.jmh.annotations.*;

@State(Scope.Benchmark)
public class MessageBench {

@Benchmark
public Message builder() {
return Message.createNew()
.email(getString())
.assignee(getString())
.conversationId(getString())
.escalationEmail(getString())
.subject(getString())
.userId(getString())
.create();
}

@Benchmark
public Message immutable() {
return new Message()
.email(getString())
.assignee(getString())
.conversationId(getString())
.escalationEmail(getString())
.subject(getString())
.userId(getString());
}

@Benchmark
@Fork(jvmArgs = "-XX:-EliminateAllocations")
public Message immutableNoOpt() {
return new Message()
.email(getString())
.assignee(getString())
.conversationId(getString())
.escalationEmail(getString())
.subject(getString())
.userId(getString());
}

private String getString() {
return "hello";
}
}

这是结果。 builderimmutable 性能相同,每次迭代仅分配 40 个字节(恰好是一个 Message 对象的大小)。

Benchmark                                        Mode  Cnt     Score     Error   Units
MessageBench.builder avgt 10 6,232 ± 0,111 ns/op
MessageBench.immutable avgt 10 6,213 ± 0,087 ns/op
MessageBench.immutableNoOpt avgt 10 41,660 ± 2,466 ns/op

MessageBench.builder:·gc.alloc.rate.norm avgt 10 40,000 ± 0,001 B/op
MessageBench.immutable:·gc.alloc.rate.norm avgt 10 40,000 ± 0,001 B/op
MessageBench.immutableNoOpt:·gc.alloc.rate.norm avgt 10 280,000 ± 0,001 B/op

关于java - jit会优化新对象吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38521299/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com