java – jit会优化新对象吗?
我创建了这个类是不可变的,并且具有流畅的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,userId); } public Message assignee(String a) { return new Message(email,a,userId); } public Message conversationId(String c) { return new Message(email,c,userId); } public Message subject(String s) { return new Message(email,s,userId); } public Message userId(String u) { return new Message(email,u); } } 我的问题是,当像这样创建一个新对象时,优化器是否能够避免大量的对象创建: Message m = new Message() .email("foo@bar.com") .assignee("bar@bax.com") .subject("subj"); 相反,制作一个单独的可变构建器对象有什么好处吗? 更新2: 更新: 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,userId); } } public static MessageBuilder createNew() { return new MessageBuilder(); } public Message(String email,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,userId); } public Message escalationEmail(String e) { return new Message(email,userId); } public Message assignee(String a) { return new Message(email,userId); } public Message conversationId(String c) { return new Message(email,userId); } public Message subject(String s) { return new Message(email,userId); } public Message userId(String u) { return new Message(email,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启动会导致第一个字符串变慢(我尝试了两种方式). 使用“new String”代码总共慢了许多倍(我不得不减少n),这可能表明正在进行“新消息”的某些优化,而不是“新字符串”. 解决方法
是的,HotSpot JIT可以消除本地环境中的冗余分配.
此优化由JDK 6u23启用的Escape Analysis提供.它经常与堆栈分配混淆,但实际上它更强大,因为它不仅允许在堆栈上分配对象,而且通过用变量(标量替换)替换对象字段来完全消除分配.优化. 优化由-XX:EliminateAllocations JVM选项控制,默认情况下为ON. 由于分配消除优化,您创建Message对象的示例都以相同的方式有效地工作.他们不分配中间对象;只是最后一个. 您的基准测试显示误导性结果,因为它收集了许多common pitfalls的微基准测试: >它在一个方法中包含了几个基准; 让我们用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"; } } 结果如下. builder和immutable都执行相同的操作,每次迭代只分配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,001 B/op MessageBench.immutableNoOpt:·gc.alloc.rate.norm avgt 10 280,001 B/op (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |