加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – jit会优化新对象吗?

发布时间:2020-12-15 04:35:16 所属栏目:Java 来源:网络整理
导读:我创建了这个类是不可变的,并且具有流畅的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
我创建了这个类是不可变的,并且具有流畅的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:
在阅读了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,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的微基准测试:

>它在一个方法中包含了几个基准;
>它测量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";
    }
}

结果如下. 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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读