覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

普通にコンストラクタ呼び出すのとリフレクション使うのとでどれだけ速度差あるの?

Javaでインスタンス生成する場合は、コンストラクタを実行するのが普通ですが、リフレクションでもやれます。

ただ、一般的にリフレクション使うと遅いというのが懸念点の一つとして上がってきます。

というわけで、普通にコンストラクタ実行するとのリフレクション使うのでどんな差があるのかをJMH使って測定してみます


測定条件

測定するインスタンス生成方法は以下の通りです

  • 普通のコンストラクタ実行
  • Class#newInstance(リフレクションを利用)
  • Constructor#newInstance(リフレクションを利用)


インスタンス化するクラスは以下です(デフォルトコンストラクタのみ持ちます)

final class Person {
    private long id;
    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


実行環境のJavaは、Java7です


JMHでの測定用のコードは以下の通りとなります

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;


public class NewInstanceBenchmark {

    @Benchmark
    public void normal() {
        Person p = new Person();
    }

    @Benchmark
    public void clazzNewInstance() throws Exception {
        Person p = Person.class.newInstance();
    }

    @Benchmark
    public void constructorNewInstance() throws Exception {
        Person p = Person.class.getDeclaredConstructor().newInstance();
    }

    static final class Person {
        private long id;
        private String name;

        public long getId() {
            return id;
        }

        public void setId(long id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(NewInstanceBenchmark.class.getSimpleName())
                .warmupIterations(20)
                .forks(1)
                .mode(Mode.Throughput)
                .build();
        new Runner(opt).run();
    }
}


測定結果

JMHの実行結果は以下の通りです

# Run complete. Total time: 00:02:02

Benchmark                                     Mode  Cnt           Score          Error  Units
NewInstanceBenchmark.clazzNewInstance        thrpt   20     7833794.167 ±    96565.569  ops/s
NewInstanceBenchmark.constructorNewInstance  thrpt   20     2182452.929 ±    70285.111  ops/s
NewInstanceBenchmark.normal                  thrpt   20  3600508098.995 ± 18204543.613  ops/s

上記結果は以下のスループットを表しています

  • NewInstanceBenchmark.clazzNewInstance ⇒ Class#newInstance で インスタンス生成
  • NewInstanceBenchmark.constructorNewInstance ⇒ Constructor#newInstance で インスタンス生成
  • NewInstanceBenchmark.normal ⇒ 普通のコンストラクタ(デフォルトコンストラクタ)実行 で インスタンス生成


Java7環境で測定したというのも関係しているとは思いますが、普通にコンストラクタ実行するのとリフレクション使うのでは雲泥の差です。
本測定においては、普通のコンストラクタ呼び出しとClass#newInstanceでは450倍以上の違いが出ています。
やはり、リフレクションは必要最低限の場面で使うべきかなと感じました。



関連エントリ