Java中的优雅降级以避免内存不足错误

时间:2011-06-22 02:05:07

标签: java performance memory

在内存密集型请求突发期间,可以使用哪些工具或最佳实践来优雅地降级Java服务中的服务?有问题的应用程序是多线程的。处理每个请求所需的工作量可能差异很大,并且不容易拆分和并行化。

我担心编写与堆使用和GC有关的应用程序级代码,但我们发现应用程序可以通过承担多个密集请求来解决问题,即内存不足错误或完整GC。通常,完整的GC无法找到任何可以释放的内存。

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

赞赏任何想法或建议。

5 个答案:

答案 0 :(得分:1)

正如joeslice所说,通过简单的资源池实现限制。在最基本的层面上,这是一个信号量 - 你的工作线程需要在处理请求之前获得许可。既然你说你有异构任务,你可能希望许可证稍微复杂一些,例如获得与工作规模成比例的一些许可证。

在过去,我发现这并不总是有效。假设你的启发式关闭,你的应用程序无论如何都会抛出一个OOM。重要的是要防止进程在糟糕的状态中徘徊,因此请立即终止并重新启动进程。有一些方法可以注意到OOM何时发生,例如见java out of memory then exit

答案 1 :(得分:1)

以下是Netty(link)的作者的实现示例。它们基本上跟踪内存使用情况,并根据该统计信息直接进行限制。

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

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

答案 2 :(得分:0)

我想知道是否有办法预先确定你将为给定的工作使用多少内存....如果有某种方法可以确定特定输入可能产生爆炸性的内存大小,也许你可以尽量避免在另一个高使用率工作的同时运行它。

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

当然,如果你不能预先确定有关待消耗内存大小的任何信息,你仍然可以探索进一步细分问题的方法,这样你就可以做大量的小内存工作而不是少数大工作。

答案 3 :(得分:0)

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

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

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

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

答案 4 :(得分:0)

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