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

发布于 2024-12-03 12:56:37 字数 690 浏览 0 评论 0原文

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

private static Logger logger = null;

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

这个想法是类可以将调试内容记录到记录器中。第一个需要记录某些内容的代码调用 getLogger() 并使记录器存在。

这种模式有一些我不喜欢的地方。首先单例 getLogger() 是不同步的,同步它,而正确的会无缘无故地给后续的每次调用带来负担。

我真的希望能够将其压缩为这样:

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

然后我可以直接引用记录器,甚至不必费心使用单例 getter。

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

We have a lot of class code which has some boilerplate like the following:

private static Logger logger = null;

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

The idea is that the class can log debug stuff into the Logger. The first code which needs to log something invokes getLogger() and that brings the logger into existence.

There are a couple of things I don't like about this pattern. First the singleton getLogger() is not synchronized and synchronizing it, while correct would put a burden on each subsequent call for no reason.

I really want to be able to condense it down to just this:

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

Then I can just reference logger directly and not even bother with a singleton getter.

The problem I fear is that by doing this I cause a Logger to be created when the class is loaded even if the logger is never called. I have 10,000+ odd classes all calling getLogger(), so how many instances of Logger am I actually creating here? If my log4j properties contains a few appenders am I just referencing the same logger over and over, or I am I creating 10,000 of these things?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

锦爱 2024-12-10 12:56:37

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

我可以给你一个题外话的建议吗?考虑使用 SLF4J+Logback 库。它们由同一位作者编写,并被描述为“流行 log4j 项目的继承者,继承了 log4j 的不足”。您可以在这个SO线程中阅读更多内容。

If you use the default Log4j configuration (i.e. default LoggerRepository, DefaultCategoryFactory etc.) then you will create 10'000 Logger instances. How much memory do they consume? No one except God and your Profiler knows this. (And my guess only the latter one would tell that to you).

If the memory footprint would be too much for your environment, move Logger initialization to the static inner class like this:

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

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

That way the instance of Logger will be only created on the first getLogger call. (This technique is known as the Initialization On Demand Holder (IODH), it is thread-safe and has zero synchronization overhead).

And may I give you one offtopic suggestion? Consider to replace Log4J with the combination of SLF4J+Logback libraries. They are written by the very same authors and described as "a successor to the popular log4j project, picking up where log4j leaves off". You can read more in this SO thread.

北渚 2024-12-10 12:56:37

仅当该类实际初始化时(即使用),它才会创建对象。到那时,每个类单个对象的小开销真的那么重要吗?

最简单的答案:尝试两种方法,看看您是否可以观察到性能/内存/等方面的任何显着差异。我怀疑您是否能够做到,但如果可以,您将能够根据< em>数据最合适的行动方案是什么。

It will only create the objects if the class is actually initialized, i.e. if it's used. At that point, does the small overhead of a single object per class really matter that much?

Simplest answer: try it both ways, and see if you can observe any significant differences in performance / memory / etc. I doubt that you'll be able to, but if you can, you'll be able to decide then based on data what the most appropriate course of action is.

つ低調成傷 2024-12-10 12:56:37

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

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

You are creating a separate logger instance for each class you load, however I believe they have a small memory footprint. Log4J is known to be optimized extensively, and I doubt they haven't thought about memory usage.

Overall, if you have 10K different classes, your app is huge - I doubt you will notice any significant difference in memory consumption. But the best is of course to measure it both ways, in your concrete environment.

浅浅淡淡 2024-12-10 12:56:37

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

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

不过,不必担心内存使用情况。为每个类提供一个额外的对象不应该是一个令人担忧的开销(希望 Logger 对象不要太大)

OP's unsynchronized getLogger() method is ok, ince Logger is thread safe. (hopefully. Since Logger is mutable, and designed to be used concurrently, I'll be surprised if its reference cannot be published safely without additional synchronization.)

Occasionally creating 2 loggers for the same class isn't a problem either.

The concern of memory usage isn't necessary though. Having an extra object for each class shouldn't be a concerning overhead (hopefully the Logger object is not too huge)

蒗幽 2024-12-10 12:56:37

您想要的方式(静态最终字段)通常是这样做的,并且由 PMD< 推荐/a>.如果您有一个记录器,那么它很有可能被使用,因此延迟初始化它不会在内存方面获得任何好处,当然在性能方面也不会获得太多好处。

不过,我会将其大写 LOGGER,因为它是一个常量。

The way you want it (static final field) is what is usually done, and recommended by PMD. If you have a logger, there is a good chance it's used, so initializing it lazily won't gain anything in terms of memory, and certainly not much in performance.

I would capitalize it LOGGER, though, since it's a constant.

只是我以为 2024-12-10 12:56:37

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

只是会使其延迟加载,但不要认为这是一个很大的收获。数以千计的对象在非常短的时间内被创建和销毁。

Static variables initialized once and same object is used for all instances. You don't really need that private static Logger getLogger() method for singleton pattern.

Just will make it lazy loaded but don't think that it is a big gain. Thounsands of objects are created and destroyed in really small amounts of time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文