从静态最终变量初始值设定项获取Logger是否有效?

时间:2011-09-06 11:10:29

标签: java log4j

我们有很多类代码,它们有一些类似以下的样板:

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

然后我可以直接引用记录器,甚至不用单独的getter。

我担心的问题是,通过这样做,即使从未调用过记录器,我也会在加载类时创建一个Logger。我有10,000多个奇怪的类都调用了getLogger(),所以我实际上在这里创建了多少个Logger实例?如果我的log4j属性包含几个appender,我只是一遍又一遍地引用相同的记录器,或者我是在创建10,000个这样的东西?

6 个答案:

答案 0 :(得分:2)

只有在实际初始化类时才会创建对象,即如果它是使用的。那时,每个类的单个对象的小开销真的很重要吗?

最简单的答案:尝试两种方式,看看你是否能观察到性能/记忆/等方面的任何重大差异。我怀疑你能否,但如果可以的话,你将能够决定那么基于数据最合适的行动方式。

答案 1 :(得分:2)

如果您使用默认的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),它是线程安全的,并且没有同步开销。)

我可以给你一个offtopic建议吗?考虑将Log4J替换为SLF4J + Logback库的组合。它们由同一作者撰写,并描述为“a successor to the popular log4j project, picking up where log4j leaves off”。您可以在this SO thread中阅读更多内容。

答案 2 :(得分:1)

您正在为每个加载的类创建一个单独的记录器实例,但我相信它们的内存占用量很小。众所周知,Log4J被广泛优化,我怀疑他们没有考虑内存使用情况。

总的来说,如果你有10K不同的类,你的应用程序是巨大的 - 我怀疑你会注意到内存消耗的任何显着差异。但最好的办法是在你的具体环境中以两种方式衡量它。

答案 3 :(得分:0)

静态变量初始化一次,同一个对象用于所有实例。对于单例模式,您实际上不需要private static Logger getLogger()方法。

只是会让它懒得装,但不要认为这是一个很大的收获。在很短的时间内创造和销毁对象的玄武。

答案 4 :(得分:0)

你想要的方式(静态最终字段)是通常所做的,并由PMD推荐。如果你有一个记录器,很有可能它被使用,所以懒惰地初始化它不会在内存方面获得任何东西,当然也没有太多的性能。

我会把它LOGGER大写,因为它是一个常数。

答案 5 :(得分:0)

OP的非同步getLogger()方法没问题,ince Logger是线程安全的。 (希望如此。由于Logger是可变的,并且设计为同时使用,如果没有额外的同步,它的引用无法安全发布,我会感到惊讶。)

偶尔为同一个类创建2个记录器也不是问题。

但是不必担心内存使用问题。为每个类创建一个额外的对象不应该是一个有关的开销(希望Logger对象不是太大)