Подтвердить что ты не робот

Кэширование класса .forName()

В одном из моих приложений я использую следующее:

public void calculate (String className)
{
  ... 
  Class clazz = Class.forName(className);
  ...
}

Эта функция вызывается несколько раз/секунду. Существует около 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);
  }
  ...
}

Будет ли это усиление производительности или это действительно не имеет значения?

Заранее благодарю

4b9b3361

Ответ 1

Я написал немного script для вычисления времени выполнения обеих функций.

Это основной класс, который я использовал.

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);
  }
}

Вот классическая реализация, использующая 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;
      }
    }
  }

Вот вторая реализация, которая использует 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;
    }
  }

Результаты:

  • 1100 мс без кэширования.
  • всего лишь 15 мс с кешированием.

Вывод:

  • Это значительная разница → да!
  • Это имеет значение для моего приложения → совсем нет.

Ответ 2

Будет ли это усиление производительности или это действительно не имеет значения?

Я был бы удивлен, если бы это имело существенное значение - и если вы только называете это "несколько раз в секунду" (а не, скажем, миллион), это действительно не стоит оптимизировать.

Вы должны, по крайней мере, попробовать это изолированно в бенчмарке, прежде чем перейти к этому более сложному дизайну. Я бы сильно ожидал, что Class.forName будет кэшировать это в любом случае, и добавление дополнительной сложности в ваше приложение не принесет пользы.

Ответ 3

Class.forName() выполняет две вещи:

  • он извлекает загруженный класс из класса loader
  • Если такой класс не найден, он пытается его загрузить.

Часть № 1 довольно быстрая. # 2 - где начинается настоящая работа (где JVM может попасть на жесткий диск или даже в сеть, в зависимости от загрузчика классов). И если вы передадите одни и те же параметры в, то все, кроме первых вызовов, никогда не смогут перейти к шагу № 2.

Итак, нет: возможно, это не стоит оптимизировать.

Ответ 4

Нет, не стоит. Класс .forName не будет загружать один и тот же класс дважды, но попытается найти класс среди загруженных классов. Это сделано на собственном уровне и должно быть очень эффективным.