容器中的Java:資源分配準則

短短几年內,容器就改變了軟件行業的面貌。也許您已經到了在容器中運行Java的地步。那很棒!不幸的是,關於容器化Java應用程序的CPU和內存使用率,還有一些事情要注意,我將在下面概述。安全

本文假定整體上熟悉Java和容器。若是您須要更多背景知識,請查看部分(或所有)參考資料。性能

Heap Space

若是有一個關於在容器中運行Java的關鍵聲明,則爲如下內容:spa

不要爲在容器中運行的任何JAVA程序手動設置JVM堆空間。相反的是,設置容器限制。code

爲何?進程

  • 首先,設置容器限制能夠實現容器(cgroups)的最基本目標,即隔離進程集合的資源使用。例如,當您經過JVM參數手動分配堆空間時,您徹底忽視了容器的限制。
  • 它容許輕鬆調整容器資源分配。須要更多內存嗎?達到容器極限。須要更少嗎?縮小尺寸。這爲自動處理相應地擴展容器(例如k8s垂直容器自動縮放器)打開了大門,而無需手動調整JVM參數。
  • 若是在容器編排環境(例如Kubernetes)中運行,則容器限制對於節點運行情況和調度都將變得極爲重要。調度程序將使用這些限制來找到合適的節點來運行容器,並確保相等的負載分佈在各個節點上。若是經過JVM參數設置內存使用率,則此信息對於調度程序不可用,所以調度程序不知道如何有效地分散容器的負載。
  • 若是未設置容器限制,而且Java在沒有顯式設置任何JVM內存標誌的容器中運行,則JVM會自動將最大堆設置爲運行它的節點上RAM的25%。例如,若是您的容器在64 GB的節點上運行,則JVM進程堆空間能夠最大爲16 GB。若是您在一個節點上運行10個容器(因爲自動擴展,這種狀況很常見),那麼您忽然會急需160 GB的RAM。這是一場隨時可能發生的災難。

你能爲這個作什麼?

設置容器內存(和CPU)限制。僅依靠資源請求(軟限制)是不夠的。軟限制很是適合幫助調度程序,可是設置硬限制可使Docker(或您使用的任何容器運行時)分配指定的資源給容器自己,而再也不分配更多資源。這也將容許Java(從Java 8u191開始默認爲「容器感知」)根據放置在容器自己上的資源限制(而不是容器正在運行的節點)正確分配內存。內存

關於[Min | Max | Initial] RAMPercentage參數

在較新的Java版本中,引入瞭如下JVM參數(並將其反向移植到Java 8u191)。資源

  • -XX:MinRAMPercentage
  • -XX:MaxRAMPercentage
  • -XX:InitialRAMPercentage

我不會詳細介紹它們是如何工做的,可是關鍵要點在於它們能夠用於微調JVM堆大小,而無需直接設置堆大小。也就是說,容器仍然能夠依靠施加在其上的限制。it

那麼使用什麼正確的值呢?答案是-「取決於」……尤爲是對容器施加的限制。容器

默認狀況下,JVM堆獲取容器內存的25%。您能夠調整初始/最小/最大堆參數來更改此…例如設置-XX:MaxRAMPercentage = 50將容許JVM爲堆消耗50%的容器內存,而不是默認的25%。何時安全,很大程度上取決於容器必須使用多少內存以及容器中正在運行哪些進程。擴展

例如,若是您的容器正在運行一個Java進程,併爲其分配了4 GB的RAM,而且您設置了-XX:MaxRAMPercentage = 50,則JVM堆將得到2 GB。這與一般會得到的1 GB默認值相反。在這種狀況下,幾乎能夠確定50%是絕對安全的,甚至是最佳的,由於許多可用RAM可能未獲得充分利用。可是,假設同一容器僅分配了512 MB RAM。如今設置-XX:MaxRAMPercentage = 50將爲堆提供256 MB的RAM,而僅將其他256 MB留給整個容器的其他部分。該內存將須要由容器中運行的全部其餘進程以及JVM Metaspace / PermGen等分配共享。在這種狀況下,也許50%不太安全。

所以,我提一下幾點建議:

  • 除非您想爲Java進程壓縮一些額外的內存空間,不然不要觸摸這些參數。在大多數狀況下,25%的默認值是管理內存的安全方法。它可能不是最有效的內存使用方法,可是RAM很便宜,所以最好仍是謹慎一點,而不是出於某些未知緣由而使JVM進程被OOM殺死。
  • 若是您確實想調整這些,請保守一點。 50%最有多是一個安全值,能夠在大多數狀況下避免出現問題,可是,這仍然很大程度上取決於容器的大小(從內存角度)。我不建議您將其設置爲75%,除非您的容器具備至少512 MB的RAM(最好是1 GB),而且您對相關應用程序的內存使用狀況有很好的瞭解。
  • 若是您的容器除Java外還運行多個進程,請在調整這些值時格外當心。容器內存在全部這些進程之間共享,而且在這些狀況下了解容器的整體使用狀況更爲複雜。
  • 超過90%的人均可能會自找麻煩。

那Metaspace / PermGen / etc呢?

這超出了本文的範圍,但請放心,也能夠對此進行調整,但可能不該該這樣作。默認的JVM行爲適用於大多數用例。若是您發現本身試圖解決一個晦澀的內存問題,那麼多是時候考慮擺弄JVM內存這個有些深奧的領域了,可是不然我將避免直接作任何事情。

CPU哪?

這裏沒什麼可作的。從Java 8u191開始,JVM默認狀況下是「容器感知」的,而且能夠正確解釋CPU份額分配。儘管有一些細節值得咱們理解,因此除了在這裏概述以外,我將指導您閱讀這篇出色的文章,詳細說明全部這些。

總結

現代Java很是適合在容器環境中運行,可是每一個人都應該知道一些不太明顯的細節,以確保他們從應用程序中得到最佳性能。我但願這裏提供的信息以及出色的參考資料能夠幫助您實現這一目標。

相關文章
相關標籤/搜索