编写应该“永远”运行的 Java 程序时要考虑啥

Posted

技术标签:

【中文标题】编写应该“永远”运行的 Java 程序时要考虑啥【英文标题】:What to consider when writing a java program that is supposed to run 'forever'编写应该“永远”运行的 Java 程序时要考虑什么 【发布时间】:2013-10-17 21:19:40 【问题描述】:

我必须编写一个被认为“永远”运行的程序,这意味着它不会定期终止。直到现在,我总是编写可以在一天结束时运行和终止的程序。程序必须进行一些同步,暂停 n 分钟,然后再次同步。

AFAIK 我目前的实现应该没有问题,理论上它应该可以正常运行,但我缺乏任何实际经验。

那么,是否有任何“模式”或最佳实践来编写非常健壮且资源高效且运行时间非常长的 Java 程序?例如运行一个月/一年后可能会出现什么问题?

一些背景:

Java:1.7,但编译到 1.5 操作系统:Windows(具体版本尚未确定)

提前致谢

【问题讨论】:

谷歌关键词:java daemon process. 您需要考虑程序是否需要能够在运行时自行更新。 【参考方案1】:

只是我在编写此类应用程序时必须牢记的所有事情的脑筋急转弯。

避免内存泄漏

我有一个应用程序,每天中午运行一次,其中我有一个FileWriter。我没有正确关闭它,然后我们开始想知道为什么我们的虚拟机在几周后会崩溃。内存泄漏实际上可以以任何形式出现,最常见的例子之一是您没有适当地de-reference 一个对象。例如,使用类的字段作为临时存储的方法。通常类会持续存在,引用也会持续存在。这让你只剩下对象,坐在记忆中,什么都不做。

使用正确的调度程序

我在该应用程序中使用了 java Timer,后来我了解到,当另一个应用程序正在更改系统时钟时,最好使用 ScheduledThreadPoolExecutor。因此,如果您打算让它完全基于 Java,我强烈建议您在 Timer 上使用它,原因在 this question 中详述。

注意内存使用情况和您的环境

如果您的应用每天都在加载大量数据,并且您有其他应用在同一台服务器上运行,那么您可能需要注意时间安排。例如,假设在中午,三个应用程序运行它们预定的操作,我会说在任何其他时间运行它可能是一个聪明的举动。注意执行代码的环境。

错误处理

您可能希望对应用进行配置,以便在出现问题时通知您,而不会导致应用崩溃。如果它每隔几个小时在某个时间运行,这意味着人们可能会依赖它,所以我会在你的 Java 代码中添加一个函数,它会向你发送一封电子邮件,详细说明异常的性质。

使其可配置

同样,如果它需要在一天中的不同时间点运行,您不希望不得不将它拉下来几个小时来对您的代码进行一些小的更改。相反,将其移植到 java 属性文件中,或者移植到 XML 配置中(或者实际上,等等)。这样做的好处是您可以在任何人真正注意到差异之前更新您的程序并启动并运行它。

害怕static关键字

即使你破坏了它们的父引用,那个坏男孩也会让对象持久存在。如果您不小心,它就是所有内存泄漏的根源。常量很好,您知道不需要更改并且需要存在于项目中才能正常运行的东西,但是如果您将它用于项目中的随机值,您很快就会想知道为什么您的应用每隔几个小时就会崩溃一次,而不是 syncing

感谢@X86 让我想起那个。

【讨论】:

和 -- 明智地使用“static”关键字...您不希望使用静态字段过度妨碍 JVM。 “即使你破坏了它们的父引用,那个坏小子也会让对象持久存在。如果你不小心,它就是所有内存泄漏的根源。” - 非常有趣......你应该写一本书......:P @Michal - 天哪,我怎么会错过这个 - :P static 方法也有问题吗?还是此提示仅适用于字段? 加载类时会加载静态方法。按照最小化您使用的内存量的逻辑,可能建议将它们最小化,因为在虚拟机存在之前它们不会从内存中取出,在这种情况下永远不会。【参考方案2】:

内存泄漏可能是最大的问题。确保在您的逻辑迭代后没有长期引用。即使是一个相对较小的对象被永久引用,最终也会耗尽内存(更糟糕的是,如果增长率为 1GB/月,在测试期间将更难检测到)。一种可能会有所帮助的方法是使用分析器的快照功能:在暂停期间拍摄快照,让同步运行几次,然后拍摄另一个快照。比较这些应该可以显示同步之间的增量,应该是零。

缓存维护是另一个问题。缓存的整体大小需要严格限制(而通常你可以在没有短期运行的程序的情况下逃脱,因为所看到的一切都足够小而不会引起问题)。同样,正确地执行缓存失效更为重要 - 广义上讲,在您的程序仍在运行时,所有被缓存的内容变得陈旧,您需要能够检测到这一点并采取适当的措施行动。这可能会很棘手,具体取决于缓存数据的黄金来源。

我要提到的最后一件事是异常处理。对于短期运行的进程,当遇到异常时让进程终止通常就足够了,因此可以处理问题并重新运行应用程序。对于一个长期运行的过程,您可能需要比这更具防御性。考虑在线程中运行程序的某些部分,如果/当它们失败时可以重新启动*。您可能需要一个主管类型的模块,它检查其他一切是否仍在心跳,如果没有,则重新启动它。如果适合您的结构,那么使用actor 风格的库而不是Java 的标准执行程序更容易实现这一点。如果可能的话,您可能希望使用钩子(可能通过 JMX/MBeans 公开)让您稍微修改行为,以允许短期黑客/解决方法受到影响,而不必降低进程。虽然这需要相当多的远见才能准确预测几个月后会出现什么问题......

*或者更确切地说,可以在另一个线程中重新启动作业

【讨论】:

感谢您也涵盖了例外情况,我也有类似的想法。

以上是关于编写应该“永远”运行的 Java 程序时要考虑啥的主要内容,如果未能解决你的问题,请参考以下文章

读书笔记 - Effective Java02. 遇到多个构造器参数时要考虑用构建器

如何使 Java 桌面应用程序永远不会让系统休眠并在后台运行?

开发 iPhone/iPad 应用程序时要考虑的 iOS 版本

Java 《Effective Java 中文版 第2版》学习笔记 遇到多个构造器时要考虑用构建器

在数据库存储结构设计时要考虑哪些因素

Java vs Kotlin 应该使用Kotlin进行Android开发吗