为什么我的Java应用程序超过了Heroku内存限制?

为什么我的Java应用程序超过了Heroku内存限制?,java,memory,heroku,Java,Memory,Heroku,我在heroku上运行了一个Java web应用程序,它不断生成“内存配额超出”消息。该应用程序本身相当大,有很多库,但它只收到很少的请求(它只被少数用户使用,因此如果没有用户在线,系统可能在几个小时内不会收到一个请求),因此性能不是主要问题 尽管在我的应用程序中很少发生这种情况,但内存消耗始终很高: 在heroku上部署应用程序之前,我使用docker容器部署应用程序,从不担心内存设置将所有内容保留为默认设置。整个容器通常消耗大约300MB 我尝试的第一件事是通过使用-Xmx256m-Xss

我在heroku上运行了一个Java web应用程序,它不断生成“内存配额超出”消息。该应用程序本身相当大,有很多库,但它只收到很少的请求(它只被少数用户使用,因此如果没有用户在线,系统可能在几个小时内不会收到一个请求),因此性能不是主要问题

尽管在我的应用程序中很少发生这种情况,但内存消耗始终很高:

在heroku上部署应用程序之前,我使用docker容器部署应用程序,从不担心内存设置将所有内容保留为默认设置。整个容器通常消耗大约300MB

我尝试的第一件事是通过使用
-Xmx256m-Xss512k
减少内存消耗,但是这似乎没有任何效果

heroku手册建议记录一些关于垃圾收集的数据,因此使用以下标志来运行我的应用程序:
-Xmx256m-Xss512k-XX:+PrintGCDetails-XX:+PrintGCDateStamps-XX:+PrintTenuringDistribution-XX:+UseConMarkSweepGC
。这给了我以下输出:

2017-01-11T22:43:39.605180+00:00 heroku[web.1]: Process running mem=588M(106.7%)
2017-01-11T22:43:39.605545+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
2017-01-11T22:43:40.431536+00:00 app[web.1]: 2017-01-11T22:43:40.348+0000: [GC (Allocation Failure) 2017-01-11T22:43:40.348+0000: [ParNew
2017-01-11T22:43:40.431566+00:00 app[web.1]: Desired survivor size 4456448 bytes, new threshold 1 (max 6)
2017-01-11T22:43:40.431579+00:00 app[web.1]: - age   1:    7676592 bytes,    7676592 total
2017-01-11T22:43:40.431593+00:00 app[web.1]: - age   2:     844048 bytes,    8520640 total
2017-01-11T22:43:40.431605+00:00 app[web.1]: - age   3:     153408 bytes,    8674048 total
2017-01-11T22:43:40.431772+00:00 app[web.1]: : 72382K->8704K(78656K), 0.0829189 secs] 139087K->78368K(253440K), 0.0830615 secs] [Times: user=0.06 sys=0.00, real=0.08 secs] 
2017-01-11T22:43:41.298146+00:00 app[web.1]: 2017-01-11T22:43:41.195+0000: [GC (Allocation Failure) 2017-01-11T22:43:41.195+0000: [ParNew
2017-01-11T22:43:41.304519+00:00 app[web.1]: Desired survivor size 4456448 bytes, new threshold 1 (max 6)
2017-01-11T22:43:41.304537+00:00 app[web.1]: - age   1:    7271480 bytes,    7271480 total
2017-01-11T22:43:41.304705+00:00 app[web.1]: : 78656K->8704K(78656K), 0.1091697 secs] 148320K->81445K(253440K), 0.1092897 secs] [Times: user=0.10 sys=0.00, real=0.11 secs] 
2017-01-11T22:43:42.589543+00:00 app[web.1]: 2017-01-11T22:43:42.526+0000: [GC (Allocation Failure) 2017-01-11T22:43:42.526+0000: [ParNew
2017-01-11T22:43:42.589562+00:00 app[web.1]: Desired survivor size 4456448 bytes, new threshold 1 (max 6)
2017-01-11T22:43:42.589564+00:00 app[web.1]: - age   1:    6901112 bytes,    6901112 total
2017-01-11T22:43:42.589695+00:00 app[web.1]: : 78656K->8704K(78656K), 0.0632178 secs] 151397K->83784K(253440K), 0.0633208 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
2017-01-11T22:43:57.653300+00:00 heroku[web.1]: Process running mem=587M(106.6%)
2017-01-11T22:43:57.653498+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
不幸的是,我不是阅读这些日志的专家,但乍一看,应用程序实际上并没有消耗大量内存,这可能是一个问题(或者我是不是严重误读了日志?)

我的
Procfile
内容如下:

web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT --context-xml context.xml app.war
更新 正如codefinger建议的那样。由于某种原因,在添加java代理之后,问题不再出现。但是现在我有了bean,能够捕捉到这个问题。在以下情况下,除内存限制仅在短时间内被超过外:

2017-01-24T10:30:00.143342+00:00 app[web.1]: measure.mem.jvm.heap.used=92M measure.mem.jvm.heap.committed=221M measure.mem.jvm.heap.max=233M
2017-01-24T10:30:00.143399+00:00 app[web.1]: measure.mem.jvm.nonheap.used=77M measure.mem.jvm.nonheap.committed=78M measure.mem.jvm.nonheap.max=0M
2017-01-24T10:30:00.143474+00:00 app[web.1]: measure.threads.jvm.total=41 measure.threads.jvm.daemon=24 measure.threads.jvm.nondaemon=2 measure.threads.jvm.internal=15
2017-01-24T10:30:00.147542+00:00 app[web.1]: measure.mem.linux.vsz=4449M measure.mem.linux.rss=446M

2017-01-24T10:31:00.143196+00:00 app[web.1]: measure.mem.jvm.heap.used=103M measure.mem.jvm.heap.committed=251M measure.mem.jvm.heap.max=251M
2017-01-24T10:31:00.143346+00:00 app[web.1]: measure.mem.jvm.nonheap.used=101M measure.mem.jvm.nonheap.committed=103M measure.mem.jvm.nonheap.max=0M
2017-01-24T10:31:00.143468+00:00 app[web.1]: measure.threads.jvm.total=42 measure.threads.jvm.daemon=25 measure.threads.jvm.nondaemon=2 measure.threads.jvm.internal=15
2017-01-24T10:31:00.153106+00:00 app[web.1]: measure.mem.linux.vsz=4739M measure.mem.linux.rss=503M

2017-01-24T10:31:24.163943+00:00 heroku[web.1]: Process running mem=517M(101.2%)
2017-01-24T10:31:24.164150+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)

2017-01-24T10:32:00.143066+00:00 app[web.1]: measure.mem.jvm.heap.used=108M measure.mem.jvm.heap.committed=248M measure.mem.jvm.heap.max=248M
2017-01-24T10:32:00.143103+00:00 app[web.1]: measure.mem.jvm.nonheap.used=108M measure.mem.jvm.nonheap.committed=110M measure.mem.jvm.nonheap.max=0M
2017-01-24T10:32:00.143173+00:00 app[web.1]: measure.threads.jvm.total=40 measure.threads.jvm.daemon=23 measure.threads.jvm.nondaemon=2 measure.threads.jvm.internal=15
2017-01-24T10:32:00.150558+00:00 app[web.1]: measure.mem.linux.vsz=4738M measure.mem.linux.rss=314M

2017-01-24T10:33:00.142989+00:00 app[web.1]: measure.mem.jvm.heap.used=108M measure.mem.jvm.heap.committed=248M measure.mem.jvm.heap.max=248M
2017-01-24T10:33:00.143056+00:00 app[web.1]: measure.mem.jvm.nonheap.used=108M measure.mem.jvm.nonheap.committed=110M measure.mem.jvm.nonheap.max=0M
2017-01-24T10:33:00.143150+00:00 app[web.1]: measure.threads.jvm.total=40 measure.threads.jvm.daemon=23 measure.threads.jvm.nondaemon=2 measure.threads.jvm.internal=15
2017-01-24T10:33:00.146642+00:00 app[web.1]: measure.mem.linux.vsz=4738M measure.mem.linux.rss=313M
在以下情况下,超出限制的时间更长:

2017-01-25T08:14:06.202429+00:00 heroku[web.1]: Process running mem=574M(111.5%)
2017-01-25T08:14:06.202429+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
2017-01-25T08:14:26.924265+00:00 heroku[web.1]: Process running mem=574M(111.5%)
2017-01-25T08:14:26.924265+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
2017-01-25T08:14:48.082543+00:00 heroku[web.1]: Process running mem=574M(111.5%)
2017-01-25T08:14:48.082615+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
2017-01-25T08:15:00.142901+00:00 app[web.1]: measure.mem.jvm.heap.used=164M measure.mem.jvm.heap.committed=229M measure.mem.jvm.heap.max=233M
2017-01-25T08:15:00.142972+00:00 app[web.1]: measure.mem.jvm.nonheap.used=121M measure.mem.jvm.nonheap.committed=124M measure.mem.jvm.nonheap.max=0M
2017-01-25T08:15:00.143019+00:00 app[web.1]: measure.threads.jvm.total=40 measure.threads.jvm.daemon=23 measure.threads.jvm.nondaemon=2 measure.threads.jvm.internal=15
2017-01-25T08:15:00.149631+00:00 app[web.1]: measure.mem.linux.vsz=4740M measure.mem.linux.rss=410M
2017-01-25T08:15:09.339319+00:00 heroku[web.1]: Process running mem=574M(111.5%)
2017-01-25T08:15:09.339319+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
2017-01-25T08:15:30.398980+00:00 heroku[web.1]: Process running mem=574M(111.5%)
2017-01-25T08:15:30.399066+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
2017-01-25T08:15:51.140193+00:00 heroku[web.1]: Process running mem=574M(111.5%)
2017-01-25T08:15:51.140280+00:00 heroku[web.1]: Error R14 (Memory quota exceeded)
2017-01-25T08:16:00.143016+00:00 app[web.1]: measure.mem.jvm.heap.used=165M measure.mem.jvm.heap.committed=229M measure.mem.jvm.heap.max=233M
2017-01-25T08:16:00.143084+00:00 app[web.1]: measure.mem.jvm.nonheap.used=121M measure.mem.jvm.nonheap.committed=124M measure.mem.jvm.nonheap.max=0M
2017-01-25T08:16:00.143135+00:00 app[web.1]: measure.threads.jvm.total=40 measure.threads.jvm.daemon=23 measure.threads.jvm.nondaemon=2 measure.threads.jvm.internal=15
2017-01-25T08:16:00.148157+00:00 app[web.1]: measure.mem.linux.vsz=4740M measure.mem.linux.rss=410M
对于后面的日志,这里是更大的图片:

(由于我重新启动了服务器,内存消耗下降)


首次超过内存限制时,cron作业(spring调度)将导入CSV文件。CSV文件以10000行为一批进行处理,因此内存中引用的行永远不会超过10K行。尽管如此,由于处理了许多批,当然总体上消耗了大量内存。此外,我还尝试手动触发导入以检查是否可以重现内存消耗峰值,但我做不到:这种情况并不总是发生。

这不是一个真正的答案,但可能会有帮助:

看起来堆外内存消耗激增(或可能出现泄漏)。来源几乎可以肯定是CSV处理。这里有一篇很好的文章描述了类似的问题。

你试过了吗?你在用什么图书馆?一些库(如eCache)使用大量堆外内存。日志显示,该应用程序使用的内存为587M,超过了512Mlimit@codefinger:我在问题中添加了更多日志。我还使用了很多库,特别是spring框架周围的生态系统。但我不知道使用任何使用堆外内存的东西,但这可能是错误的。我怎么知道?