Java-buildpack: 调整 Glibc 环境变量(例如 MALLOC_ARENA_MAX)

创建于 2016-08-12  ·  12评论  ·  资料来源: cloudfoundry/java-buildpack

这是我们讨论过的另一个问题@nebhale。

最近一直在研究一些 Java 内存问题,我从 Heroku 看到了以下两篇文章(我应该称它们为“无法命名的那些”吗?:P):

调整 glibc 内存行为
测试 Cedar-14 内存使用

这表明调整MALLOC_ARENA_MAX可以帮助解决应用程序中的内存问题。 似乎它也给Hadoop带来了问题,尽管这可能是他们自己监控 VMEM 的错误(说真的,伙计们,为什么?!)。

在我自己的测试中,我注意到设置MALLOC_ARENA_MAX似乎有助于减缓内存增长,尽管它_不_完全阻止它。

设置MALLOC_ARENA_MAX会对性能产生影响(我一直将其设置为2 ,正如 Heroku 所建议的那样),但结果如下:

  • 新线程创建,要求一个竞技场
  • 如果 < MALLOC_ARENA_MAX块正在使用中(默认为8 * cpu_count ),则获取一个。
  • 如果 >,分享一个现有的
  • malloc ing新内存时,如果独占一个arena,不需要加锁。
  • 如果是共享的,就得加锁,从而造成内存争用。 在glibc引入 arenas 之前,所有线程都在争夺相同的内存。
  • 当线程退出时, glibc将竞技场标记为“空闲”,但不会_不_将其返回给操作系统。

无论如何,这是我的理解。 所以我认为CF上的大部分Java应用基本不受影响,原因如下:

  • 有限的线程抖动。 如果一个主要问题,可以将容器配置为具有min_threads = max_threads 。 大多数线程都是长期存在的,并且会处理大量请求。
  • Java 不会在每个线程的基础上执行malloc - 它在启动时mallocs堆空间,然后根据需要写入它。 据我所知,一旦线程运行,就不需要执行操作系统级别的malloc ,这意味着我们永远不会有操作系统级别的内存争用(这已被拉入 Java 运行时)。

    • NIO 和ByteBuffer之类的东西可能会导致malloc调用-对此不确定。

综上所述,在深入研究之后,似乎设置MALLOC_ARENA_MAX可能只是隐藏了一个更深层次的问题,我不确定我是否会建议默认设置它。 它似乎并没有对性能产生负面影响,但是我测试的两个应用程序在设计上实际上都是非性能的,所以不要把它当作福音。

我最初认为这里可能存在内存问题,但我不再那么确定了。 我认为如果发生内存泄漏,会发生以下情况:

无限MALLOC_ARENAS_MAX

  • 线程要求竞技场
  • 线程获取竞技场
  • 线程填满竞技场,从不释放内存
  • 线程要求新的竞技场
  • 重复直到cgroup OOM 杀死他。

MALLOC_ARENAS_MAX设置为2

  • 线程要求竞技场
  • 线程获取竞技场
  • 线程填满竞技场,从不释放内存
  • 线程要求新的竞技场,但没有更多可用的。
  • 线程改为写入“主”竞技场(或本例中的本机程序堆),这是无界的。 如果有足够的时间, cgroup OOM 杀手锏。

(实际的分配决策要复杂得多——这已大大简化。请参阅了解 glibc malloc以获得深入的解释)。

这可以解释为什么我看到pmap报告的堆空间(它_不是_Java堆!)在我的一次测试运行中上升和上升 ​​- 本机代码的某处存在内存泄漏,竞技场得到了填满了,一旦它们满了,它就不得不回退到使用本机堆。

尽管如此,可能应该为各个线程使用的本机内存留出一些余量。 如果它足够小,它可能会留在计算器的“原生”部分。 否则,由于它与使用的线程数有关,因此可能需要以某种方式计算。

不管设置它的决定如何,我_do_ 认为记录它会非常有用(以及它与 Java 的关系等),就像提供指向其他glibc配置环境变量的链接一样。 查找这些信息并不像我希望的那样容易。 我认为这也应该记录在其他构建包中——尤其是 Ruby 和 Go,更有可能看到内存领域的问题——请注意,Heroku 示例都谈到了 Ruby。

最有用的评论

你好呀,

我注意到 TieredCompilation 似乎正在导致持续增长。 禁用 TieredCompilation (-XX:-TieredCompilation) 已经阻止了这种增长在我们的几个应用程序(全部使用 jdk8)上发生。 我今天向 Oracle 提交了一个错误以进行评估,这可能与已经提到的错误有关。 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8164293

只是想我会提到它,以防这对您有所帮助。

/dom

所有12条评论

@ipsi你知道 #160 和 #163 吗?

我刚刚收到来自 SAP 的@TimGerlach的一条推文,他似乎也对这方面的改进感兴趣。 @TimGerlach ,您能详细说明您的要求吗?

这可以解释为什么我看到 pmap 报告的堆空间(不是 Java 堆!)在我的一次测试运行中上升和上升 ​​- 本机代码的某处存在内存泄漏,arenas 被填满,有一次它们已经满了,它不得不回退到使用本机堆。

您是否查看过 Evan Jones 撰写的这篇文章

TL;DR:始终关闭 GZIPInputStream 和 GZIPOutputStream,因为它们通过 zlib 使用本机内存。 要追踪泄漏,请使用 jemalloc 并使用 MALLOC_CONF 环境变量打开采样分析。

最近 Heroku发表了一篇关于在 Kafka 中追踪类似错误的博客。 GOV UK GDS 团队关于调试本机内存泄漏的文章也可能会有所帮助。

Evan Jones 还写了一篇关于 Java 的 ByteBuffer 中的本机内存泄漏的博客

感谢@lhotari提到并指出我这个问题。 我将详细说明#319,因为它与我们的主题更相关。

@lhotari我知道第一个问题-不知道第二个问题。 据我所知,实际上存在一个 HotSpot 错误,这可能也是您所看到的: http ://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8164293

我看到了 GZIP 的东西,甚至用 JEMalloc 运行了我的应用程序,但没有看到他们报告的相同问题。 鉴于此问题 (a) 仅出现在 Java 8 中,并且 (b) 在禁用 HotSpot 时消失,我怀疑有多个问题可能导致应用程序随着时间的推移增加内存。

@ipsi感谢您指出 JDK 错误。

您是否看过我在https://github.com/cloudfoundry/java-buildpack/issues/163#issue -60842702 中提出的假设?
一种假设是调整CodeCacheExpansionSizeMinMetaspaceExpansion也会减少 malloc 内存碎片。
必须有多个因素导致进程 RSS 增长缓慢的相同症状。 这是提交 #163 的主要原因。

你好呀,

我注意到 TieredCompilation 似乎正在导致持续增长。 禁用 TieredCompilation (-XX:-TieredCompilation) 已经阻止了这种增长在我们的几个应用程序(全部使用 jdk8)上发生。 我今天向 Oracle 提交了一个错误以进行评估,这可能与已经提到的错误有关。 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8164293

只是想我会提到它,以防这对您有所帮助。

/dom

仅供参考,我提出的错误已在https://bugs.openjdk.java.net/browse/JDK-8164293中标记为已解决,目标版本为 8u152,预计发布日期为 2017-10-16。

嗯,这是一个有趣的约会。

仅供参考,该错误已被移植到已发布的 8u131: https ://bugs.openjdk.java.net/browse/JDK-8178124

嗯,这是个好消息。 我将把它开放几周,以便人们针对 v3.16 和 v4.0 进行测试,并让我知道是否仍有未解决的问题。

没有投诉,所以我要关闭它。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

edeandrea picture edeandrea  ·  4评论

CAFxX picture CAFxX  ·  13评论

ghost picture ghost  ·  26评论

bingosummer picture bingosummer  ·  4评论

aknobloch picture aknobloch  ·  8评论