偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Java和Docker限制的那些事兒

開發(fā) 后端
Java和Docker不是天然的朋友。 Docker可以設(shè)置內(nèi)存和CPU限制,而Java不能自動(dòng)檢測(cè)到。使用Java的Xmx標(biāo)識(shí)(繁瑣/重復(fù))或新的實(shí)驗(yàn)性JVM標(biāo)識(shí),我們可以解決這個(gè)問題。

Java和Docker不是天然的朋友。 Docker可以設(shè)置內(nèi)存和CPU限制,而Java不能自動(dòng)檢測(cè)到。使用Java的Xmx標(biāo)識(shí)(繁瑣/重復(fù))或新的實(shí)驗(yàn)性JVM標(biāo)識(shí),我們可以解決這個(gè)問題。

虛擬化中的不匹配

Java和Docker的結(jié)合并不是完美匹配的,最初的時(shí)候離完美匹配有相當(dāng)大的距離。對(duì)于初學(xué)者來(lái)說(shuō),JVM的全部設(shè)想就是,虛擬機(jī)可以讓程序與底層硬件無(wú)關(guān)。

那么,把我們的Java應(yīng)用打包到JVM中,然后整個(gè)再塞進(jìn)Docker容器中,能給我們帶來(lái)什么好處呢?大多數(shù)情況下,你只是在復(fù)制JVMs和Linux容器,除了浪費(fèi)更多的內(nèi)存,沒任何好處。感覺這樣子挺傻的。

[[240474]]

不過(guò),Docker可以把你的程序,設(shè)置,特定的JDK,Linux設(shè)置和應(yīng)用服務(wù)器,還有其他工具打包在一起,當(dāng)做一個(gè)東西。站在DevOps/Cloud的角度來(lái)看,這樣一個(gè)完整的容器有著更高層次的封裝。

問題一:內(nèi)存

時(shí)至今日,絕大多數(shù)產(chǎn)品級(jí)應(yīng)用仍然在使用Java 8(或者更舊的版本),而這可能會(huì)帶來(lái)問題。Java 8(update 131之前的版本)跟Docker無(wú)法很好地一起工作。問題是在你的機(jī)器上,JVM的可用內(nèi)存和CPU數(shù)量并不是Docker允許你使用的可用內(nèi)存和CPU數(shù)量。

比如,如果你限制了你的Docker容器只能使用100MB內(nèi)存,但是呢,舊版本的Java并不能識(shí)別這個(gè)限制。Java看不到這個(gè)限制。JVM會(huì)要求更多內(nèi)存,而且遠(yuǎn)超這個(gè)限制。如果使用太多內(nèi)存,Docker將采取行動(dòng)并殺死容器內(nèi)的進(jìn)程!JAVA進(jìn)程被干掉了,很明顯,這并不是我們想要的。

為了解決這個(gè)問題,你需要給Java指定一個(gè)最大內(nèi)存限制。在舊版本的Java(8u131之前),你需要在容器中通過(guò)設(shè)置-Xmx來(lái)限制堆大小。這感覺不太對(duì),你可不想定義這些限制兩次,也不太想在你的容器中來(lái)定義。

幸運(yùn)的是我們現(xiàn)在有了更好的方式來(lái)解決這個(gè)問題。從Java 9之后(8u131+),JVM增加了如下標(biāo)志:

  1. -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap 

這些標(biāo)志強(qiáng)制JVM檢查L(zhǎng)inux的cgroup配置,Docker是通過(guò)cgroup來(lái)實(shí)現(xiàn)最大內(nèi)存設(shè)置的。現(xiàn)在,如果你的應(yīng)用到達(dá)了Docker設(shè)置的限制(比如500MB),JVM是可以看到這個(gè)限制的。JVM將會(huì)嘗試GC操作。如果仍然超過(guò)內(nèi)存限制,JVM就會(huì)做它該做的事情,拋出OutOfMemoryException。也就是說(shuō),JVM能夠看到Docker的這些設(shè)置。

從Java 10之后(參考下面的測(cè)試),這些體驗(yàn)標(biāo)志位是默認(rèn)開啟的,也可以使用-XX:+UseContainerSupport來(lái)使能(你可以通過(guò)設(shè)置-XX:-UseContainerSupport來(lái)禁止這些行為)。

問題二:CPU

第二個(gè)問題是類似的,但它與CPU有關(guān)。簡(jiǎn)而言之,JVM將查看硬件并檢測(cè)CPU的數(shù)量。它會(huì)優(yōu)化你的runtime以使用這些CPUs。但是同樣的情況,這里還有另一個(gè)不匹配,Docker可能不允許你使用所有這些CPUs??上У氖?,這在Java 8或Java 9中并沒有修復(fù),但是在Java 10中得到了解決。

從Java 10開始,可用的CPUs的計(jì)算將采用以不同的方式(默認(rèn)情況下)解決此問題(同樣是通過(guò)UseContainerSupport)。

Java和Docker的內(nèi)存處理測(cè)試

作為一個(gè)有趣的練習(xí),讓我們驗(yàn)證并測(cè)試Docker如何使用幾個(gè)不同的JVM版本/標(biāo)志甚至不同的JVM來(lái)處理內(nèi)存不足。

首先,我們創(chuàng)建一個(gè)測(cè)試應(yīng)用程序,它只是簡(jiǎn)單地“吃”內(nèi)存并且不釋放它。

  1. java 
  2. import java.util.ArrayList; 
  3. import java.util.List; 
  4. public class MemEat { 
  5.     public static void main(String[] args) { 
  6.         List l = new ArrayList<>(); 
  7.         while (true) { 
  8.             byte b[] = new byte[1048576]; 
  9.             l.add(b); 
  10.             Runtime rt = Runtime.getRuntime(); 
  11.             System.out.println( "free memory: " + rt.freeMemory() ); 
  12.         } 
  13.     } 

我們可以啟動(dòng)Docker容器并運(yùn)行這個(gè)應(yīng)用程序來(lái)查看會(huì)發(fā)生什么。

測(cè)試一:Java 8u111

首先,我們將從具有舊版本Java 8的容器開始(update 111)。

  1. shell 
  2. docker run -m 100m -it java:openjdk-8u111 /bin/bash 

我們編譯并運(yùn)行MemEat.java文件:

  1. shell 
  2. javac MemEat.java 
  3. java MemEat 
  4. ... 
  5. free memory: 67194416 
  6. free memory: 66145824 
  7. free memory: 65097232 
  8. Killed 

正如所料,Docker已經(jīng)殺死了我們的Java進(jìn)程。不是我們想要的(!)。你也可以看到輸出,Java認(rèn)為它仍然有大量的內(nèi)存需要分配。

我們可以通過(guò)使用-Xmx標(biāo)志為Java提供最大內(nèi)存來(lái)解決此問題:

  1. shell 
  2. javac MemEat.java 
  3. java -Xmx100m MemEat 
  4. ... 
  5. free memory: 1155664 
  6. free memory: 1679936 
  7. free memory: 2204208 
  8. free memory: 1315752 
  9. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  10.     at MemEat.main(MemEat.java:8) 

在提供了我們自己的內(nèi)存限制之后,進(jìn)程正常停止,JVM理解它正在運(yùn)行的限制。然而,問題在于你現(xiàn)在將這些內(nèi)存限制設(shè)置了兩次,Docker一次,JVM一次。

測(cè)試二:Java 8u144

如前所述,隨著增加新標(biāo)志來(lái)修復(fù)問題,JVM現(xiàn)在可以遵循Docker所提供的設(shè)置。我們可以使用版本新一點(diǎn)的JVM來(lái)測(cè)試它。

  1. shell 
  2. docker run -m 100m -it adoptopenjdk/openjdk8 /bin/bash 

(在撰寫本文時(shí),此OpenJDK Java鏡像的版本是Java 8u144)

接下來(lái),我們?cè)俅尉幾g并運(yùn)行MemEat.java文件,不帶任何標(biāo)志:

  1. shell 
  2. javac MemEat.java 
  3. java MemEat 
  4. ... 
  5. free memory: 67194416 
  6. free memory: 66145824 
  7. free memory: 65097232 
  8. Killed 

依然存在同樣的問題。但是我們現(xiàn)在可以提供上面提到的實(shí)驗(yàn)性標(biāo)志來(lái)試試看:

  1. shell 
  2. javac MemEat.java 
  3. java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap MemEat 
  4. ... 
  5. free memory: 1679936 
  6. free memory: 2204208 
  7. free memory: 1155616 
  8. free memory: 1155600 
  9. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  10. at MemEat.main(MemEat.java:8) 

這一次我們沒有告訴JVM限制的是什么,我們只是告訴JVM去檢查正確的限制設(shè)置!現(xiàn)在感覺好多了。

測(cè)試三:Java 10u23

有些人在評(píng)論和Reddit上提到Java 10通過(guò)使實(shí)驗(yàn)標(biāo)志成為新的默認(rèn)值來(lái)解決所有問題。這種行為可以通過(guò)禁用此標(biāo)志來(lái)關(guān)閉:-XX:-UseContainerSupport。

當(dāng)我測(cè)試它時(shí),它最初不起作用。在撰寫本文時(shí),AdoptAJDK OpenJDK10鏡像與jdk-10+23一起打包。這個(gè)JVM顯然還是不理解UseContainerSupport標(biāo)志,該進(jìn)程仍然被Docker殺死。

  1. shell 
  2. javac MemEat.java 
  3. java MemEat 
  4. ... 
  5. free memory: 96262112 
  6. free memory: 94164960 
  7. free memory: 92067808 
  8. free memory: 89970656 
  9. Killed 
  10. java -XX:+UseContainerSupport MemEat 
  11. Unrecognized VM option 'UseContainerSupport' 
  12. Error: Could not create the Java Virtual Machine. 
  13. Error: A fatal exception has occurred. Program will exit. 

測(cè)試四:Java 10u46(Nightly)

我決定嘗試AdoptAJDK OpenJDK 10的最新nightly構(gòu)建。它包含的版本是Java 10+46,而不是Java 10+23。

  1. shell 
  2. docker run -m 100m -it adoptopenjdk/openjdk10:nightly /bin/bash 

然而,在這個(gè)ngithly構(gòu)建中有一個(gè)問題,導(dǎo)出的PATH指向舊的Java 10+23目錄,而不是10+46,我們需要修復(fù)這個(gè)問題。

  1. shell 
  2. export PATH=$PATH:/opt/java/openjdk/jdk-10+46/bin/ 
  3. javac MemEat.java 
  4. java MemEat 
  5. ... 
  6. free memory: 3566824 
  7. free memory: 2796008 
  8. free memory: 1480320 
  9. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  10.   at MemEat.main(MemEat.java:8) 

成功!不提供任何標(biāo)志,Java 10依然可以正確檢測(cè)到Dockers內(nèi)存限制。

測(cè)試五:OpenJ9

我最近也在試用OpenJ9,這個(gè)免費(fèi)的替代JVM已經(jīng)從IBM J9開源,現(xiàn)在由Eclipse維護(hù)。

請(qǐng)?jiān)谖业南乱黄┪模╤ttp://royvanrijn.com/blog/2018/05/openj9-jvm-shootout/)中閱讀關(guān)于OpenJ9的更多信息。

它運(yùn)行速度快,內(nèi)存管理非常好,性能卓越,經(jīng)??梢詾槲覀兊奈⒎?wù)節(jié)省多達(dá)30-50%的內(nèi)存。這幾乎可以將Spring Boot應(yīng)用程序定義為’micro’了,其運(yùn)行時(shí)間只有100-200mb,而不是300mb+。我打算盡快就此寫一篇關(guān)于這方面的文章。

但令我驚訝的是,OpenJ9還沒有類似于Java 8/9/10+中針對(duì)cgroup內(nèi)存限制的標(biāo)志(backported)的選項(xiàng)。如果我們將以前的測(cè)試用例應(yīng)用到最新的AdoptAJDK OpenJDK 9 + OpenJ9 build:

  1. shell 
  2. docker run -m 100m -it adoptopenjdk/openjdk9-openj9 /bin/bash 

我們添加OpenJDK標(biāo)志(OpenJ9會(huì)忽略的標(biāo)志):

  1. shell 
  2. java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap MemEat 
  3. ... 
  4. free memory: 83988984 
  5. free memory: 82940400 
  6. free memory: 81891816 
  7. Killed 

Oops,JVM再次被Docker殺死。

我真的希望類似的選項(xiàng)將很快添加到OpenJ9中,因?yàn)槲蚁M谏a(chǎn)環(huán)境中運(yùn)行這個(gè)選項(xiàng),而不必指定最大內(nèi)存兩次。 Eclipse/IBM正在努力修復(fù)這個(gè)問題,已經(jīng)提了issues,甚至已經(jīng)針對(duì)issues提交了PR。

更新:(不推薦Hack)

一個(gè)稍微丑陋/hacky的方式來(lái)解決這個(gè)問題是使用下面的組合標(biāo)志:

  1. shell 
  2. java -Xmx`cat /sys/fs/cgroup/memory/memory.limit_in_bytes` MemEat 
  3. ... 
  4. free memory: 3171536 
  5. free memory: 2127048 
  6. free memory: 2397632 
  7. free memory: 1344952 
  8. JVMDUMP039I Processing dump event "systhrow", detail "java/lang/OutOfMemoryError" at 2018/05/15 14:04:26 - please wait. 
  9. JVMDUMP032I JVM requested System dump using '//core.20180515.140426.125.0001.dmp' in response to an event 
  10. JVMDUMP010I System dump written to //core.20180515.140426.125.0001.dmp 
  11. JVMDUMP032I JVM requested Heap dump using '//heapdump.20180515.140426.125.0002.phd' in response to an event 
  12. JVMDUMP010I Heap dump written to //heapdump.20180515.140426.125.0002.phd 
  13. JVMDUMP032I JVM requested Java dump using '//javacore.20180515.140426.125.0003.txt' in response to an event 
  14. JVMDUMP010I Java dump written to //javacore.20180515.140426.125.0003.txt 
  15. JVMDUMP032I JVM requested Snap dump using '//Snap.20180515.140426.125.0004.trc' in response to an event 
  16. JVMDUMP010I Snap dump written to //Snap.20180515.140426.125.0004.trc 
  17. JVMDUMP013I Processed dump event "systhrow", detail "java/lang/OutOfMemoryError"
  18. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  19.   at MemEat.main(MemEat.java:8) 

在這種情況下,堆大小受限于分配給Docker實(shí)例的內(nèi)存,這適用于較舊的JVM和OpenJ9。這當(dāng)然是錯(cuò)誤的,因?yàn)槿萜鞅旧砗投淹獾腏VM的其他部分也使用內(nèi)存。但它似乎工作,顯然Docker在這種情況下是寬松的。也許某些bash大神會(huì)做出更好的版本,從其他進(jìn)程的字節(jié)中減去一部分。

無(wú)論如何,不要這樣做,它可能無(wú)法正常工作。

測(cè)試六:OpenJ9(Nightly)

有人建議使用OpenJ9的最新nightly版本。

  1. shell 
  2. docker run -m 100m -it adoptopenjdk/openjdk9-openj9:nightly /bin/bash 

最新的OpenJ9夜間版本,它有兩個(gè)東西:

另一個(gè)有問題的PATH參數(shù),需要先解決這個(gè)問題

JVM支持新標(biāo)志UseContainerSupport(就像Java 10一樣)

  1. shell 
  2. export PATH=$PATH:/opt/java/openjdk/jdk-9.0.4+12/bin/ 
  3. javac MemEat.java 
  4. java -XX:+UseContainerSupport MemEat 
  5. ... 
  6. free memory: 5864464 
  7. free memory: 4815880 
  8. free memory: 3443712 
  9. free memory: 2391032 
  10. JVMDUMP039I Processing dump event "systhrow", detail "java/lang/OutOfMemoryError" at 2018/05/15 21:32:07 - please wait. 
  11. JVMDUMP032I JVM requested System dump using '//core.20180515.213207.62.0001.dmp' in response to an event 
  12. JVMDUMP010I System dump written to //core.20180515.213207.62.0001.dmp 
  13. JVMDUMP032I JVM requested Heap dump using '//heapdump.20180515.213207.62.0002.phd' in response to an event 
  14. JVMDUMP010I Heap dump written to //heapdump.20180515.213207.62.0002.phd 
  15. JVMDUMP032I JVM requested Java dump using '//javacore.20180515.213207.62.0003.txt' in response to an event 
  16. JVMDUMP010I Java dump written to //javacore.20180515.213207.62.0003.txt 
  17. JVMDUMP032I JVM requested Snap dump using '//Snap.20180515.213207.62.0004.trc' in response to an event 
  18. JVMDUMP010I Snap dump written to //Snap.20180515.213207.62.0004.trc 
  19. JVMDUMP013I Processed dump event "systhrow", detail "java/lang/OutOfMemoryError"
  20. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 

TADAAA,正在修復(fù)中!

奇怪的是,這個(gè)標(biāo)志在OpenJ9中默認(rèn)沒有啟用,就像它在Java 10中一樣。再說(shuō)一次:確保你測(cè)試了這是你想在一個(gè)Docker容器中運(yùn)行Java。

結(jié)論

簡(jiǎn)言之:注意資源限制的不匹配。測(cè)試你的內(nèi)存設(shè)置和JVM標(biāo)志,不要假設(shè)任何東西。

如果您在Docker容器中運(yùn)行Java,請(qǐng)確保你設(shè)置了Docker內(nèi)存限制和在JVM中也做了限制,或者你的JVM能夠理解這些限制。

如果您無(wú)法升級(jí)您的Java版本,請(qǐng)使用-Xmx設(shè)置您自己的限制。

對(duì)于Java 8和Java 9,請(qǐng)更新到最新版本并使用:

  1. -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap 

對(duì)于Java 10,確保它支持’UseContainerSupport’(更新到最新版本)。

對(duì)于OpenJ9(我強(qiáng)烈建議使用,可以在生產(chǎn)環(huán)境中有效減少內(nèi)存占用量),現(xiàn)在使用-Xmx設(shè)置限制,但很快會(huì)出現(xiàn)一個(gè)支持UseContainerSupport標(biāo)志的版本。

責(zé)任編輯:武曉燕 來(lái)源: 程序員
相關(guān)推薦

2022-11-04 07:57:59

編程編碼編譯器

2021-05-22 09:44:21

PythonNumpy數(shù)組Python矩陣

2021-03-18 09:01:53

軟件開發(fā)軟件選型

2023-04-11 07:34:40

分布式系統(tǒng)算法

2021-06-09 13:28:40

密碼安全身份認(rèn)證數(shù)據(jù)安全

2013-12-26 14:23:03

定位系統(tǒng)GPS監(jiān)測(cè)

2021-02-22 08:39:03

Java關(guān)鍵字Java基礎(chǔ)

2018-09-26 06:50:19

2021-06-02 08:33:31

TPCTPC-H系統(tǒng)

2011-02-25 14:35:00

2022-02-08 17:39:04

MySQL服務(wù)器存儲(chǔ)

2010-10-15 10:31:00

2021-02-01 14:17:53

裝飾器外層函數(shù)里層函數(shù)

2021-03-09 23:12:51

Python集合項(xiàng)目

2021-10-13 08:53:53

Zookeeper存儲(chǔ)系統(tǒng)

2016-06-07 10:47:42

2022-04-14 11:50:39

函數(shù)組件hook

2021-09-04 16:12:33

壓縮算法數(shù)據(jù)

2018-02-02 13:58:59

數(shù)據(jù)存儲(chǔ)

2010-04-07 13:13:19

Visual Stud
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)