Эффективно ли получить Logger из инициализатора статической конечной переменной? - PullRequest
7 голосов
/ 06 сентября 2011

У нас есть много кода класса, у которого есть некоторый шаблон, подобный следующему:

private static Logger logger = null;

private static Logger getLogger() {
  if (logger == null) {
    logger = Logger.getLogger(MyClass.class);
  }
  return logger;
}

Идея состоит в том, что класс может регистрировать отладочную информацию в Logger.Первый код, который должен что-то регистрировать, вызывает getLogger (), и это приводит к появлению регистратора.

Есть несколько вещей, которые мне не нравятся в этом шаблоне.Сначала синглтон getLogger () не синхронизируется и не синхронизирует его, в то время как корректный вызов будет обременительным для каждого последующего вызова без какой-либо причины.

Я действительно хочу иметь возможность сжать его до следующего:

private static final Logger logger = Logger.getLogger(MyClass.class);

Тогда я могу просто ссылаться на регистратор напрямую и даже не беспокоиться об одноэлементном геттере.

Проблема, которую я боюсь, состоит в том, что, делая это, я вызываю создание Logger, когда класс загружается дажеесли регистратор никогда не вызывается.У меня более 10000 нечетных классов, все они вызывают getLogger (), так сколько экземпляров Logger я на самом деле создаю здесь?Если в моих свойствах log4j есть несколько дополнений, я просто снова и снова ссылаюсь на один и тот же регистратор, или я создаю 10 000 таких вещей?

Ответы [ 6 ]

2 голосов
/ 06 сентября 2011

Если вы используете конфигурацию Log4j по умолчанию (то есть по умолчанию LoggerRepository, DefaultCategoryFactory и т. Д.), То вы создадите 10 000 экземпляров Logger. Сколько памяти они потребляют? Никто, кроме Бога и вашего Профилировщика, не знает этого. (И я думаю, что только последний скажет вам это).

Если объем памяти будет слишком большим для вашей среды, переместите инициализацию Logger в статический внутренний класс следующим образом:

static class LoggerHolder {
  static Logger logger = Logger.getLogger(MyClass.class);
}

private static Logger getLogger() {
  return LoggerHolder.logger;
}

Таким образом, экземпляр Logger будет создан только при первом вызове getLogger. (Этот метод известен как держатель инициализации по требованию (IODH), он ориентирован на многопотоковое исполнение и имеет нулевые накладные расходы на синхронизацию).

И могу ли я дать вам одно оффтопное предложение? Попробуйте заменить Log4J на комбинацию библиотек SLF4J + Logback . Они написаны теми же авторами и описаны как "a successor to the popular log4j project, picking up where log4j leaves off". Вы можете прочитать больше в этой теме .

2 голосов
/ 06 сентября 2011

Он будет создавать объекты только в том случае, если класс действительно инициализирован, т. Е. Если он используется .На этом этапе действительно ли так важны небольшие накладные расходы одного объекта на класс?

Самый простой ответ: попробуйте оба способа, и посмотрите, сможете ли вы заметить какие-либо существенные различия в производительности / памяти / и т.д. Iсомневаюсь, что сможешь, но если сможешь, тогда ты сможешь решить, основываясь на данных , каков наиболее подходящий вариант действий.

1 голос
/ 06 сентября 2011

Вы создаете отдельный экземпляр логгера для каждого загружаемого вами класса, однако я считаю, что они занимают мало места в памяти.Известно, что Log4J сильно оптимизирован, и я сомневаюсь, что они не задумывались об использовании памяти.

В целом, если у вас 10К различных классов, ваше приложение огромно - я сомневаюсь, что вы заметите какую-либо значительную разницу в памятипотребление.Но лучше всего, конечно, измерить это в обоих направлениях в конкретной среде.

0 голосов
/ 07 сентября 2011
Метод

несинхронизированный getLogger() в порядке, ince Logger безопасен для потоков. (надеюсь. Поскольку Logger является изменяемым и предназначен для одновременного использования, я буду удивлен, если его ссылку нельзя будет безопасно опубликовать без дополнительной синхронизации.)

Иногда создание 2 регистраторов для одного и того же класса также не проблема.

Хотя использование памяти не обязательно. Наличие дополнительного объекта для каждого класса не должно вызывать проблем (возможно, объект Logger не слишком большой)

0 голосов
/ 06 сентября 2011

Как вы хотите (статическое конечное поле), это то, что обычно делается, и рекомендуется PMD .Если у вас есть регистратор, есть хороший шанс, что он будет использован, поэтому его ленивая инициализация не принесет ничего с точки зрения памяти и, конечно, не слишком высокой производительности., поскольку это константа.

0 голосов
/ 06 сентября 2011

Статические переменные инициализируются один раз, и один и тот же объект используется для всех экземпляров.Вам действительно не нужен этот private static Logger getLogger() метод для одноэлементного паттерна.

Просто загрузит его лениво, но не думайте, что это большой выигрыш.Громадные пески объектов создаются и уничтожаются за очень небольшие промежутки времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...