Class.forName()缓存

bvd*_*vdb 15 java reflection performance

在我的一个应用程序中,我使用以下内容:

public void calculate (String className)
{
  ... 
  Class clazz = Class.forName(className);
  ...
}
Run Code Online (Sandbox Code Playgroud)

该函数被调用几次/秒.大约有10个可能的类名.虽然我确实意识到这个函数中有一些内部缓存,但我认为这个缓存只能在本机级别上使用.

出于这个原因,我开始怀疑是否应该添加自己的缓存.

private static Map<String,Class> classMap;

public void calculate (String className)
{
  ... 
  Class clazz = classMap.get(className);
  if (clazz == null) 
  {
      clazz = Class.forName(className);
      if (classMap == null) classMap = new HashMap<String, Class>(40);
      classMap.put(className, clazz);
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

这会是性能提升还是真的没有区别?

先感谢您

bvd*_*vdb 12

我写了一个小脚本来计算两个函数的执行时间.

这是我使用的Main类.

public class Benchmark
{
  public static void main(String... pArgs)
  {
    // prepare all data as much as possible.
    // we don't want to do this while the clock is running.
    Class[] classes = {Object.class, Integer.class, String.class, Short.class, Long.class, Double.class,
                       Float.class, Boolean.class, Character.class, Byte.class};
    int cycles = 1000000;
    String[] classNames = new String[cycles];
    for (int i = 0; i < cycles; i++) 
    {
      classNames[i] = classes[i % classes.length].getName();
    }

    // THERE ARE 2 IMPLEMENTATIONS - CLASSIC vs CACHING
    Implementation impl = new Caching();   // or Classic();

    // Start the clocks !
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < cycles; i++)
    {
      impl.doStuff(classNames[i]);
    }
    long endTime = System.currentTimeMillis();

    // calculate and display result
    long totalTime = endTime - startTime;
    System.out.println(totalTime);
  }
}
Run Code Online (Sandbox Code Playgroud)

这是使用的经典实现 Class.forName

  private interface Implementation
  {
    Class doStuff(String clzName);
  }

  private static class Classic implements Implementation
  {
    @Override
    public Class doStuff(String clzName)
    {
      try
      {
        return Class.forName(clzName);
      }
      catch (Exception e)
      {
        return null;
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

这是第二个使用a HashMap来缓存Class对象的实现.

  private static class Caching implements Implementation
  {
    private Map<String, Class> cache = new HashMap<String, Class>();

    @Override
    public Class doStuff(String clzName)
    {
      Class clz = cache.get(clzName);
      if (clz != null) return clz;
      try
      {
        clz = Class.forName(clzName);
        cache.put(clzName, clz);
      }
      catch (Exception e)
      {
      }
      return clz;
    }
  }
Run Code Online (Sandbox Code Playgroud)

结果:

  • 1100毫秒没有缓存.
  • 缓存只有15毫秒.

结论:

  • 这是一个显着的差异 - >是的!
  • 这对我的申请是否重要 - >根本不重要.

  • 使用`System.nanoTime`进行高精度*差*测量.(如果你明白我的意思,它就是一个秒表,而不是一个时钟.)不,睡觉对预热没有帮助 - 你想要运用你将要计时的代码,这样JIT就有机会优化在你衡量之前.有一个很好的图书馆可以帮助解决所有这些问题,但我现在不记得这个名字...... (5认同)
  • 请注意,您不应该使用`currentTimeMillis`进行基准测试,并且您应该包括JIT编译等的预热期.它可能不会更改*非常*,但值得了解.我不会在这里抓住例外,要么...... (2认同)

Jon*_*eet 9

这会是性能提升还是真的没有区别?

如果它产生了显着的差异,我会感到惊讶 - 如果你只是"每秒几次"调用它(而不是比如说一百万次),那么它真的不值得优化.

在承诺这个更复杂的设计之前,你至少应该基准测试中单独尝试这个.Class.forName无论如何,我强烈期望将其缓存,并在您的应用中添加更多复杂性并不好.


Joa*_*uer 6

Class.forName() 做两件事:

  1. 它从类加载器中获取一个加载的类
  2. 如果没有找到这样的类,它会尝试加载它。

第 1 部分非常快。#2 是真正的工作开始的地方(JVM 可能会访问硬盘甚至网络,具体取决于类加载器)。如果您传入相同的参数,那么除了第一次调用之外的所有调用都将永远不会到达第 2 步。

所以不:它可能不值得优化。