有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我们有很多类代码,其中有一些样板如下:

private static Logger logger = null;

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

其想法是,该类可以将调试内容记录到记录器中。需要记录某些内容的第一个代码调用getLogger(),这将使记录器成为现实

这种模式有几点我不喜欢。首先,singleton getLogger()没有同步,并且正在同步它,而correct会无缘无故地给后续的每个调用增加负担

我真的想把它浓缩成这样:

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

然后,我就可以直接引用logger,甚至不用麻烦使用singleton getter

我担心的问题是,这样做会导致在加载类时创建记录器,即使从未调用记录器。我有10000多个类都在调用getLogger(),那么我在这里实际创建了多少个Logger实例呢?如果我的log4j属性包含几个appender,我是在一遍又一遍地引用同一个记录器,还是在创建10000个这样的东西


共 (6) 个答案

  1. # 1 楼答案

    如果使用默认的Log4j配置(即默认的LoggerRepository、DefaultCategoryFactory等),则将创建10000个记录器实例。它们消耗了多少内存?除了上帝和你的剖析者没有人知道这一点。(我猜只有后者会告诉你)

    如果内存占用对您的环境来说太多,请将记录器初始化移动到静态内部类,如下所示:

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

    这样Logger的实例只会在第一次getLogger调用时创建。(这种技术被称为按需初始化持有者(IODH),它是线程安全的,没有同步开销)

    我可以给你一个离题的建议吗?考虑用SLF4J+Logback库的组合替换Log4J。它们是由同样的作者写的,被描述为“^{”。你可以在this SO thread中阅读更多内容

  2. # 2 楼答案

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

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

    不过,对内存使用的担忧是没有必要的。每个类都有一个额外的对象不应该是一个令人担忧的开销(希望Logger对象不会太大)

  3. # 3 楼答案

    它只会在类实际初始化时创建对象,即使用。在这一点上,每个类一个对象的小开销真的那么重要吗

    最简单的答案是:两种方法都试一下,看看你是否能观察到性能/记忆等方面的任何显著差异。我怀疑你是否能够,但如果可以,你将能够根据数据决定最合适的行动方案

  4. # 4 楼答案

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

    只是会让它懒惰,但不要认为这是一个很大的收获。许多物体在很短的时间内被创造和摧毁

  5. # 5 楼答案

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

    总的来说,如果你有10万个不同的类,你的应用程序是巨大的——我怀疑你会注意到内存消耗方面的任何显著差异。但最好的方法当然是在你的具体环境中,从两个方面来衡量

  6. # 6 楼答案

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

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