简体   繁体   English

Akka Actor 是如何清理资源的?

[英]How Akka Actors are clearing resources?

We're experiencing strange memory behavior on our web server built using Akka HTTP.我们在使用 Akka Z293C9EA246FF99785DC6F62A6860 构建的 web 服务器上遇到奇怪的 memory 行为Our architecture goes like this:我们的架构是这样的:

  1. web server routes calls various actors, get results for future and streams it to response web 服务器路由调用各种参与者,获取未来的结果并将其流式传输以响应
  2. actors call non-blocking operations (using futures), combine and process data fetched from them and pipe results to sender.参与者调用非阻塞操作(使用期货),组合和处理从它们获取的数据和 pipe 结果发送给发送者。 We're using standard Akka actors, implementing its receive method (not Akka typed)我们使用标准的 Akka 演员,实现其接收方法(不是 Akka 类型)
  3. there is no blocking code anywhere in the app应用程序中的任何地方都没有阻止代码

When I run web server locally, at the start it takes around 350 MB.当我在本地运行 web 服务器时,一开始大约需要 350 MB。 After first request, memory usage jumps to around 430 MB and slowly is increasing with each request (monitored using Activity Monitor on Mac).在第一次请求之后,memory 使用量跃升至 430 MB 左右,并且随着每个请求缓慢增加(使用 Mac 上的活动监视器进行监控)。 But shouldn't GC clean things after each request?但是GC不应该在每次请求后清理东西吗? Shouldn't memory usage after processing be 350 MB again?处理后的 memory 使用量不应该再次为 350 MB 吗?

I also installed YourKit java profiler and here is a digram of head memory我还安装了 YourKit java 分析器,这里是头图 memory

.

It can be seen that once memory usage increase, it never goes back, and system is stateless.可以看出,一旦memory使用量增加,就再也回不去了,系统是无状态的。 Also, when I run GC manually from profiler, it almost doesn't do anything, just a small decrease in memory usage.此外,当我从分析器手动运行 GC 时,它几乎什么都不做,只是 memory 使用量略有减少。 I understand some services might cache things after first request, consuming memory temporarily, but is there any policy inside Akka Actors or Akka HTTP about this? I understand some services might cache things after first request, consuming memory temporarily, but is there any policy inside Akka Actors or Akka HTTP about this?

I tried to check objects furthest from GC but it only shows library classes and Akka built in classes, nothing related to our code.我试图检查离 GC 最远的对象,但它只显示库类和内置类的 Akka,与我们的代码无关。

So, I have a 2 questions:所以,我有两个问题:

  1. How the actor is closing resources and freeing memory after message processing?演员如何在消息处理后关闭资源并释放 memory? Did you experienced anything similar?你有过类似的经历吗?
  2. Is there any better way of profiling Akka HTTP which will show me stacktrace of using classed furthest from GC?有没有更好的分析 Akka HTTP 的方法,它会告诉我使用离 GC 最远的分类的堆栈跟踪?

On a side note, is it advisable to use scheduler inside Actors (running inside Akka HTTP server)?附带说明一下,是否建议在 Actor 内使用调度程序(在 Akka HTTP 服务器内运行)? When I do that, it seems memory usage increases heavily and app runs our of memory on DEV environment.当我这样做时,似乎 memory 的使用量大幅增加,并且应用程序在 DEV 环境中运行我们的 memory。

Thanks in advance,提前致谢,

Amer阿米尔

An actor remains active until it is explicitly stopped: there is no garbage collection.一个actor一直保持活动状态,直到它被明确停止:没有垃圾收集。

Probably the two most common methods for managing actor lifetimes (beyond the actor itself deciding that it's time to stop) are:管理 Actor 生命周期的两种最常用的方法(除了 Actor 本身决定是时候停止之外)可能是:

  • Parent is responsible for stopping children.家长有责任阻止孩子。 If the actors are being spawned for performing specific tasks on behalf of the parent, for instance, this approach is called for.例如,如果演员是为了代表父母执行特定任务而产生的,则需要这种方法。

  • Using an inactivity timeout.使用不活动超时。 If the actors represent domain entities (eg an actor for every user account, where this actor in some sense serves as an in-memory cache), using context.setReceiveTimeout to cause a ReceiveTimeout message to be sent to the actor after the timeout has passed (note that in some cases the scheduled send of that message may not be canceled in time if a message was enqueued in the mailbox but not processed when the timeout expired: receiving a ReceiveTimeout is not a guarantee that the timeout has passed since the last received message) is a pretty reasonable solution, especially if using Akka Persistence and Akka Cluster Sharding to allow the actor's state to be recovered.如果参与者代表域实体(例如,每个用户帐户的参与者,这个参与者在某种意义上用作内存缓存),则使用context.setReceiveTimeout导致在超时后向参与者发送ReceiveTimeout消息(请注意,在某些情况下,如果邮件已在邮箱中排队但在超时到期时未处理,则该邮件的预定发送可能不会及时取消:接收ReceiveTimeout并不能保证自上次收到后超时已过message) 是一个非常合理的解决方案,特别是如果使用 Akka Persistence 和 Akka Cluster Sharding 来恢复 actor 的 state。

Update to add:更新添加:

Regarding关于

shouldn't GC clean things after each request? GC 不应该在每次请求后清理东西吗?

The short answer is no, GC will not clean things after each request (and if it does, that's a very good sign that you haven't provisioned enough memory).简短的回答是否定的,GC 不会在每次请求后清理东西(如果确实如此,这是一个很好的迹象,表明您没有提供足够的内存)。

The longer answer is that the characteristics of garbage collection on the JVM are very underspecified: the only rule a garbage collection implementation has to respect is that it never frees an object reachable from a GC root (basically any variable on a thread's stack or static to a class) by a chain of strong references.更长的答案是,JVM 上的垃圾收集特性非常不明确:垃圾收集实现必须遵守的唯一规则是它永远不会释放可从 GC 根访问的 object(基本上是线程堆栈上的任何变量或 object 或 object)一个类)通过一个强引用链。 When and even whether the garbage collector reclaims the space taken up by garbage is entirely implementation dependent (I say "whether" to account for the existence of the Epsilon garbage collector, which never frees memory; this is useful for benchmarking JVMs without the complication of garbage collection and also in environments where the application can be restarted when it runs out of memory: the JVM crash is in this some sense the actual garbage collector).垃圾收集器何时甚至是否回收垃圾占用的空间完全取决于实现(我说“是否”是为了说明 Epsilon 垃圾收集器的存在,它永远不会释放 memory;这对于在没有复杂性的情况下对 JVM 进行基准测试很有用垃圾收集以及在应用程序用完 memory 时可以重新启动的环境中:JVM 崩溃在某种意义上是实际的垃圾收集器)。

You could try executing java.lang.System.gc when the server stops: this may cause a GC run (note that there is no requirement that the system actually collect any garbage in such a scenario).您可以尝试在服务器停止时执行java.lang.System.gc :这可能会导致 GC 运行(请注意,在这种情况下不需要系统实际收集任何垃圾)。 If a garbage collector will free any memory, about the only time it has to run is if there's not enough space to fulfill an object allocation request: therefore if the application stops allocating objects, there may not be a garbage collection run.如果垃圾收集器将释放任何 memory,那么它必须运行的唯一时间是如果没有足够的空间来满足 object 分配请求:因此,如果应用程序停止分配对象,则可能不会运行垃圾收集。

For performance reasons, most modern garbage collectors in the JVM wait until there's no more than a certain amount of free space before they collect garbage: this is because the time taken to reclaim all space is proportional to the number of objects which aren't reclaimable and for a great many applications, the pattern is that most objects are comparatively ephemeral, so the number of objects which aren't reclaimable is reasonably constant.出于性能原因,JVM 中的大多数现代垃圾收集器会等到不超过一定数量的可用空间后再收集垃圾:这是因为回收所有空间所花费的时间与不可回收的对象数量成正比对于很多应用程序来说,模式是大多数对象都是相对短暂的,因此不可回收的对象的数量是相当恒定的。 The consequence of that is that the garbage collector will do about the same amount of work in a "full" GC for a given application regardless of how much free space there is.这样做的结果是垃圾收集器将在给定应用程序的“完整”GC 中完成大约相同数量的工作,而不管有多少可用空间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM