2011-06-22 43 views
4

有什么工具或最佳实践可用于在突发的内存密集型请求期间正常降级Java服务中的服务?有问题的应用程序是多线程的。处理每个请求所需的工作量可能差别很大,并且不容易分解和并行化。Java中的优雅降级以避免内存不足错误

我对编写与堆使用和GC有关的应用程序级代码保持警惕,但我们发现应用程序可能因为承担多个密集请求而陷入麻烦,即内存不足错误或完整GC 。一个完整的GC通常无法找到任何空闲的内存。

长话短说:我正在考虑增加一些限制或排队功能来预先解决这类问题。

任何意见或建议表示赞赏。

+0

我假设你已经对应用程序进行了剖析,以确保你不能减少使用的内存量。您也检查过无法获得更大的服务器,16 GB的PC可能会花费1000美元。 –

+0

彼得 - 是的,我们对应用程序进行了剖析,以了解我们可以在哪里减少内存使用量。更多的物理内存,更大的服务器 - 这些选项可能不会导致GC在发生时更长更痛苦吗? – ChrisW

+0

GC的成本与使用量和免费量的倒数成正比。如果使用相同数量的内存,则GC暂停将相同,但更少。如果你使用更多的记忆,它会更长,但这比直接失败要好。 –

回答

0

我想知道是否有一种方法可以预先确定给定工作大约需要多少内存....如果有某种方法可以确定某个特定输入可能产生爆炸性内存大小,也许您可以试图阻止它在另一个高使用率工作的同时运行。

如果您可以确定从工作到工作的相对大小(这是一个很大的假设),您可以允许(比如说)使用计数Semaphore一次运行100个工作单元。一个典型的工作可能只能算作一个单位(并且只获得一个许可证),其中较大的工作可能需要在运行之前获得10或20个许可证。......

当然,如果您无法预先确定即将消耗的内存的大小,您可能仍然能够探索进一步细分问题的方法,以便您执行大量的小内存作业,而不是少量的大作业。

0

在应用程序服务器中,通常有工作程序线程池的设置。该池中的最大线程数大致定义了您将消耗多少内存。这是一个简单而重要的工作概念。

虽然我不称之为“优雅的退化”。这是节流。优雅的降级涉及降低服务水平(例如提供给用户的细节的数量)以至少保持对于每个当前用户可用的基本必要功能。限制额外的用户只是运气不好。

该定义的优雅降级需要知道应用程序的性质,因此您必须让代码知道它。

显而易见的方法是将所有可能的操作按用户的需要划分为类。第一类应始终处理。第2(3rd,4th,...)类只有在服务器低于特定的负载水平时才被提供,否则返回“暂时不可用”的错误。

1

正如joeslice所说,通过一个简单的资源池实施节流。在最基本的层面上,这是一个信号量 - 你的工作线程在处理请求之前需要获得许可证。既然你说你有不同的任务,你可能想让这个许可变得更复杂一些,例如获得一些与工作规模成正比的许可证。

在过去,我发现这并不总是奏效。假设你的启发式算法已关闭,并且你的应用程序抛出一个OOM。防止进程在不良状态下挂起很重要,因此请立即停止并重新启动进程。有几种方法可以注意到OOM发生的时间,例如见java out of memory then exit

+0

这个过程不一定会处于不良状态。 OOM将展开有问题的线程堆栈,释放一些内存。在线程池中,OOM可以被捕获并且启发式调整可以在某种程度上考虑到已更改的请求模式。整个应用程序可以存活并继续提供服务。 –

+1

OOM可能有各种各样的原因,其中只有一个是由信号量/启发式控制的,所以我不认为你所描述的是可靠的。换句话说,抓住OOM有时可能有效,但是我发现安全而不是抱歉好多了。保留GC和应用程序日志,以便稍后在生产环境之外重现问题! – jtoberon

1

以下是Netty(link)作者的实现示例。他们基本上跟踪内存使用情况并基于该统计信息直接进行节流。

另一个更粗糙的方法是通过使用固定线程池和有界队列来限制并发执行。一旦这个队列已满,通常的方法是让queue.put()的调用者自己执行任务。通过这种方式,负载将(一直应该)传播回客户端,直到新请求的创建变慢。因此,应用程序的行为。变得更加“优美”。

实际上,我几乎只使用上述的“粗略”方式。它工作得很好。基本上,固定线程池和有界队列+呼叫者的组合运行拒绝策略。我保持参数(队列大小,线程池大小)可配置,然后设计完成后,我会调整这些参数。有时很明显,线程池可以在服务等中共享,所以在这种情况下,使用类ThreadPoolExecutor来获得固定的线程池/有界队列/调用者运行策略都非常方便。

0

您使用的是J2EE吗?因为这是Application Server负责进行负载平衡的工作,并且我相信很多主流AppServers都支持它。你的申请不应该关心它。