Java ใช้หน่วยความจำมากกว่าขนาดฮีป (หรือขนาดที่ถูกต้อง จำกัด หน่วยความจำ Docker)


119

สำหรับแอปพลิเคชันของฉันหน่วยความจำที่ใช้โดยกระบวนการ Java นั้นมากกว่าขนาดฮีปมาก

ระบบที่คอนเทนเนอร์กำลังทำงานอยู่เริ่มมีปัญหาหน่วยความจำเนื่องจากคอนเทนเนอร์ใช้หน่วยความจำมากกว่าขนาดฮีปมาก

ขนาดฮีปตั้งไว้ที่ 128 MB ( -Xmx128m -Xms128m) ในขณะที่คอนเทนเนอร์ใช้หน่วยความจำสูงสุด 1GB ภายใต้สภาวะปกติต้องใช้ 500MB หากคอนเทนเนอร์นักเทียบท่ามีขีด จำกัด ด้านล่าง (เช่นmem_limit=mem_limit=400MB) กระบวนการจะถูกฆ่าโดยนักฆ่าหน่วยความจำที่ไม่เพียงพอของระบบปฏิบัติการ

คุณช่วยอธิบายได้ไหมว่าทำไมกระบวนการ Java ถึงใช้หน่วยความจำมากกว่าฮีป วิธีปรับขนาดขีด จำกัด หน่วยความจำ Docker ให้ถูกต้อง มีวิธีลดรอยเท้าหน่วยความจำนอกฮีปของกระบวนการ Java หรือไม่?


ผมรวบรวมรายละเอียดบางอย่างเกี่ยวกับปัญหาการใช้คำสั่งจากการติดตามหน่วยความจำพื้นเมืองใน JVM

จากระบบโฮสต์ฉันได้รับหน่วยความจำที่คอนเทนเนอร์ใช้

$ docker stats --no-stream 9afcb62a26c8
CONTAINER ID        NAME                                                                                        CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
9afcb62a26c8        xx-xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.0acbb46bb6fe3ae1b1c99aff3a6073bb7b7ecf85   0.93%               461MiB / 9.744GiB   4.62%               286MB / 7.92MB      157MB / 2.66GB      57

จากภายในคอนเทนเนอร์ฉันได้รับหน่วยความจำที่ใช้ในกระบวนการ

$ ps -p 71 -o pcpu,rss,size,vsize
%CPU   RSS  SIZE    VSZ
11.2 486040 580860 3814600

$ jcmd 71 VM.native_memory
71:

Native Memory Tracking:

Total: reserved=1631932KB, committed=367400KB
-                 Java Heap (reserved=131072KB, committed=131072KB)
                            (mmap: reserved=131072KB, committed=131072KB) 

-                     Class (reserved=1120142KB, committed=79830KB)
                            (classes #15267)
                            (  instance classes #14230, array classes #1037)
                            (malloc=1934KB #32977) 
                            (mmap: reserved=1118208KB, committed=77896KB) 
                            (  Metadata:   )
                            (    reserved=69632KB, committed=68272KB)
                            (    used=66725KB)
                            (    free=1547KB)
                            (    waste=0KB =0.00%)
                            (  Class space:)
                            (    reserved=1048576KB, committed=9624KB)
                            (    used=8939KB)
                            (    free=685KB)
                            (    waste=0KB =0.00%)

-                    Thread (reserved=24786KB, committed=5294KB)
                            (thread #56)
                            (stack: reserved=24500KB, committed=5008KB)
                            (malloc=198KB #293) 
                            (arena=88KB #110)

-                      Code (reserved=250635KB, committed=45907KB)
                            (malloc=2947KB #13459) 
                            (mmap: reserved=247688KB, committed=42960KB) 

-                        GC (reserved=48091KB, committed=48091KB)
                            (malloc=10439KB #18634) 
                            (mmap: reserved=37652KB, committed=37652KB) 

-                  Compiler (reserved=358KB, committed=358KB)
                            (malloc=249KB #1450) 
                            (arena=109KB #5)

-                  Internal (reserved=1165KB, committed=1165KB)
                            (malloc=1125KB #3363) 
                            (mmap: reserved=40KB, committed=40KB) 

-                     Other (reserved=16696KB, committed=16696KB)
                            (malloc=16696KB #35) 

-                    Symbol (reserved=15277KB, committed=15277KB)
                            (malloc=13543KB #180850) 
                            (arena=1734KB #1)

-    Native Memory Tracking (reserved=4436KB, committed=4436KB)
                            (malloc=378KB #5359) 
                            (tracking overhead=4058KB)

-        Shared class space (reserved=17144KB, committed=17144KB)
                            (mmap: reserved=17144KB, committed=17144KB) 

-               Arena Chunk (reserved=1850KB, committed=1850KB)
                            (malloc=1850KB) 

-                   Logging (reserved=4KB, committed=4KB)
                            (malloc=4KB #179) 

-                 Arguments (reserved=19KB, committed=19KB)
                            (malloc=19KB #512) 

-                    Module (reserved=258KB, committed=258KB)
                            (malloc=258KB #2356) 

$ cat /proc/71/smaps | grep Rss | cut -d: -f2 | tr -d " " | cut -f1 -dk | sort -n | awk '{ sum += $1 } END { print sum }'
491080

แอปพลิเคชันนี้เป็นเว็บเซิร์ฟเวอร์ที่ใช้ Jetty / Jersey / CDI รวมอยู่ในไขมันที่ไกลถึง 36 MB

มีการใช้ OS และ Java เวอร์ชันต่อไปนี้ (ภายในคอนเทนเนอร์) อิมเมจ Docker ขึ้นอยู่กับopenjdk:11-jre-slim.

$ java -version
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment (build 11+28-Debian-1)
OpenJDK 64-Bit Server VM (build 11+28-Debian-1, mixed mode, sharing)
$ uname -a
Linux service1 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 GNU/Linux

https://gist.github.com/prasanthj/48e7063cac88eb396bc9961fb3149b58


6
ฮีปเป็นที่ที่จัดสรรอ็อบเจ็กต์อย่างไรก็ตาม JVM มีพื้นที่หน่วยความจำอื่น ๆ มากมายรวมถึงไลบรารีที่แบ่งใช้บัฟเฟอร์หน่วยความจำโดยตรงเธรดสแต็กคอมโพเนนต์ GUI metaspace คุณต้องดูว่า JVM มีขนาดใหญ่เพียงใดและทำให้ขีด จำกัด สูงพอที่คุณจะอยากให้กระบวนการตายมากกว่าที่จะใช้อีกต่อไป
Peter Lawrey

2
ดูเหมือนว่า GC จะใช้หน่วยความจำมาก คุณสามารถลองใช้ตัวรวบรวม CMS แทน ดูเหมือนว่า ~ 125 MB จะใช้สำหรับโค้ด metaspace + แต่ถ้าไม่ลดขนาดฐานรหัสของคุณคุณก็ไม่น่าจะสามารถทำให้เล็กลงได้ พื้นที่มุ่งมั่นใกล้ถึงขีด จำกัด ของคุณจึงไม่น่าแปลกใจที่มันจะถูกฆ่า
Peter Lawrey

คุณตั้งค่า -Xms และ -Xmx ได้อย่างไร
มิก


1
คุณตั้งโปรแกรมเรียกใช้งานไฟล์จำนวนมาก (เช่นสร้างไฟล์ขนาดกิกะไบต์) หรือไม่? ถ้าเป็นเช่นนั้นคุณควรทราบว่าการcgroupsเพิ่มดิสก์แคชลงในหน่วยความจำที่ใช้แม้ว่าเคอร์เนลจะถูกจัดการและจะมองไม่เห็นสำหรับโปรแกรมผู้ใช้ก็ตาม ( psdocker stats
คำนึงถึง

คำตอบ:


207

หน่วยความจำเสมือนที่ใช้โดยกระบวนการ Java ขยายไปไกลกว่า Java Heap คุณทราบหรือไม่ว่า JVM มีเนื้อหาย่อยมากมาย: Garbage Collector, Class Loading, JIT compiler เป็นต้นและระบบย่อยทั้งหมดเหล่านี้ต้องการ RAM จำนวนหนึ่งในการทำงาน

JVM ไม่ใช่ผู้บริโภค RAM เท่านั้น ไลบรารีเนทีฟ (รวมถึงไลบรารีคลาส Java มาตรฐาน) อาจจัดสรรหน่วยความจำเนทีฟด้วย และสิ่งนี้จะไม่ปรากฏแม้แต่ใน Native Memory Tracking แอปพลิเคชัน Java เองยังสามารถใช้หน่วยความจำนอกฮีปโดยใช้ ByteBuffers โดยตรง

แล้วหน่วยความจำในกระบวนการ Java คืออะไร?

ส่วน JVM (ส่วนใหญ่แสดงโดย Native Memory Tracking)

  1. Java Heap

    ส่วนที่ชัดเจนที่สุด. นี่คือที่ที่วัตถุ Java อาศัยอยู่ ฮีปใช้-Xmxหน่วยความจำไม่เกินจำนวน

  2. คนเก็บขยะ

    โครงสร้าง GC และอัลกอริทึมต้องการหน่วยความจำเพิ่มเติมสำหรับการจัดการฮีป โครงสร้างเหล่านี้ ได้แก่ Mark Bitmap, Mark Stack (สำหรับการสำรวจกราฟวัตถุ), Remembered Sets (สำหรับบันทึกการอ้างอิงระหว่างภูมิภาค) และอื่น ๆ บางส่วนสามารถปรับแต่งได้โดยตรงเช่น-XX:MarkStackSizeMaxส่วนอื่น ๆ ขึ้นอยู่กับเลย์เอาต์ฮีปเช่นพื้นที่ G1 ที่ใหญ่กว่า ( -XX:G1HeapRegionSize) ยิ่งเป็นชุดที่จำได้น้อยกว่า

    ค่าโสหุ้ยหน่วยความจำ GC แตกต่างกันไประหว่างอัลกอริทึม GC -XX:+UseSerialGCและ-XX:+UseShenandoahGCมีค่าใช้จ่ายที่น้อยที่สุด G1 หรือ CMS อาจใช้ประมาณ 10% ของขนาดฮีปทั้งหมด

  3. รหัสแคช

    ประกอบด้วยโค้ดที่สร้างขึ้นแบบไดนามิก: เมธอดที่คอมไพล์ JIT, ตัวแปลภาษาและต้นขั้วเวลาทำงาน ขนาดถูก จำกัด โดย-XX:ReservedCodeCacheSize(240M โดยค่าเริ่มต้น) ปิด-XX:-TieredCompilationเพื่อลดจำนวนโค้ดที่คอมไพล์แล้วจึงใช้โค้ดแคช

  4. ผู้รวบรวม

    คอมไพเลอร์ JIT เองก็ต้องการหน่วยความจำในการทำงาน -XX:CICompilerCountนี้สามารถลดลงได้อีกครั้งโดยการปิดฉัตรรวบรวมหรือโดยการลดจำนวนกระทู้เรียบเรียง:

  5. กำลังโหลดคลาส

    ข้อมูลเมตาของคลาส (รหัสวิธีการ, สัญลักษณ์, พูลคงที่, คำอธิบายประกอบ ฯลฯ ) จะถูกเก็บไว้ในพื้นที่นอกฮีปที่เรียกว่า Metaspace ยิ่งมีการโหลดคลาสมากเท่าใดก็ยิ่งใช้ metaspace มากขึ้นเท่านั้น การใช้งานทั้งหมดสามารถ จำกัด ได้โดย-XX:MaxMetaspaceSize(ไม่ จำกัด โดยค่าเริ่มต้น) และ -XX:CompressedClassSpaceSize(1G โดยค่าเริ่มต้น)

  6. ตารางสัญลักษณ์

    แฮชแท็กหลักสองรายการของ JVM: ตาราง Symbol ประกอบด้วยชื่อลายเซ็นตัวระบุ ฯลฯ และตาราง String มีการอ้างอิงถึงสตริงภายใน String.internหากติดตามหน่วยความจำพื้นเมืองบ่งชี้การใช้งานหน่วยความจำอย่างมีนัยสำคัญจากตารางสตริงก็อาจหมายถึงการประยุกต์ใช้มากเกินไปโทร

  7. หัวข้อ

    เธรดสแต็กยังรับผิดชอบในการรับ RAM -Xssขนาดสแต็คจะถูกควบคุมโดย ค่าเริ่มต้นคือ 1M ต่อเธรด แต่โชคดีที่สิ่งต่างๆไม่เลวร้ายนัก ระบบปฏิบัติการจะจัดสรรเพจหน่วยความจำอย่างเฉื่อยชากล่าวคือในการใช้งานครั้งแรกดังนั้นการใช้หน่วยความจำจริงจะลดลงมาก (โดยทั่วไปคือ 80-200 KB ต่อเธรดสแต็ก) ฉันเขียนสคริปต์เพื่อประมาณว่า RSS เป็นของ Java thread stacks มากแค่ไหน

    มีส่วน JVM อื่น ๆ ที่จัดสรรหน่วยความจำเนทีฟ แต่โดยปกติแล้วจะไม่มีบทบาทสำคัญในการใช้หน่วยความจำทั้งหมด

บัฟเฟอร์โดยตรง

ByteBuffer.allocateDirectการประยุกต์ใช้อย่างชัดเจนอาจขอปิดกองหน่วยความจำโดยการโทร เริ่มต้นออกจากกองวงเงินเท่ากับแต่ก็สามารถจะถูกแทนที่ด้วย-Xmx -XX:MaxDirectMemorySizeDirect ByteBuffers จะรวมอยู่ในOtherส่วนของเอาต์พุต NMT (หรือInternalก่อน JDK 11)

จำนวนหน่วยความจำโดยตรงที่ใช้สามารถมองเห็นได้ผ่าน JMX เช่นใน JConsole หรือ Java Mission Control:

BufferPool MBean

นอกจาก ByteBuffers โดยตรงแล้วยังมีMappedByteBuffers- ไฟล์ที่แมปกับหน่วยความจำเสมือนของกระบวนการ NMT ไม่ติดตามพวกเขาอย่างไรก็ตาม MappedByteBuffers ยังสามารถใช้หน่วยความจำกายภาพ และไม่มีวิธีง่ายๆในการ จำกัด จำนวนเงินที่สามารถทำได้ คุณสามารถดูการใช้งานจริงได้โดยดูที่แผนผังหน่วยความจำกระบวนการ:pmap -x <pid>

Address           Kbytes    RSS    Dirty Mode  Mapping
...
00007f2b3e557000   39592   32956       0 r--s- some-file-17405-Index.db
00007f2b40c01000   39600   33092       0 r--s- some-file-17404-Index.db
                           ^^^^^               ^^^^^^^^^^^^^^^^^^^^^^^^

ไลบรารีดั้งเดิม

โค้ด JNI ที่โหลดโดยSystem.loadLibraryสามารถจัดสรรหน่วยความจำนอกฮีปได้มากเท่าที่ต้องการโดยไม่มีการควบคุมจากฝั่ง JVM นอกจากนี้ยังเกี่ยวข้องกับ Java Class Library มาตรฐาน โดยเฉพาะอย่างยิ่งทรัพยากร Java ที่ไม่ได้ปิดอาจกลายเป็นแหล่งที่มาของการรั่วไหลของหน่วยความจำดั้งเดิม ตัวอย่างทั่วไปคือZipInputStreamหรือDirectoryStream.

เอเจนต์ JVMTI โดยเฉพาะjdwpเอเจนต์การดีบักอาจทำให้ใช้หน่วยความจำมากเกินไป

คำตอบนี้อธิบายถึงวิธีการจัดสรรหน่วยความจำรายละเอียดพื้นเมืองที่มีasync-Profiler

ปัญหาตัวจัดสรร

โดยทั่วไปกระบวนการจะร้องขอหน่วยความจำเนทีฟโดยตรงจาก OS (โดยการmmapเรียกระบบ) หรือโดยใช้malloc- ตัวจัดสรร libc มาตรฐาน ในทางกลับกันmallocขอหน่วยความจำขนาดใหญ่จากระบบปฏิบัติการโดยใช้mmapแล้วจัดการชิ้นส่วนเหล่านี้ตามอัลกอริทึมการจัดสรรของมันเอง ปัญหาคือ - ขั้นตอนวิธีนี้สามารถนำไปสู่การกระจายตัวและมากเกินไปการใช้งานหน่วยความจำเสมือน

jemallocซึ่งเป็นตัวจัดสรรทางเลือกมักจะดูฉลาดกว่า libc ทั่วไปmallocดังนั้นการเปลี่ยนไปใช้jemallocอาจส่งผลให้มีขนาดเล็กลงโดยไม่เสียค่าใช้จ่าย

ข้อสรุป

ไม่มีวิธีการรับประกันในการประเมินการใช้หน่วยความจำแบบเต็มของกระบวนการ Java เนื่องจากมีปัจจัยที่ต้องพิจารณามากเกินไป

Total memory = Heap + Code Cache + Metaspace + Symbol tables +
               Other JVM structures + Thread stacks +
               Direct buffers + Mapped files +
               Native Libraries + Malloc overhead + ...

เป็นไปได้ที่จะย่อหรือ จำกัด พื้นที่หน่วยความจำบางส่วน (เช่น Code Cache) โดยแฟล็ก JVM แต่พื้นที่อื่น ๆ อีกมากมายไม่สามารถควบคุม JVM ได้เลย

แนวทางหนึ่งที่เป็นไปได้ในการตั้งค่าขีด จำกัด Docker คือการเฝ้าดูการใช้งานหน่วยความจำจริงในสถานะ "ปกติ" ของกระบวนการ : มีเครื่องมือและเทคนิคในการตรวจสอบปัญหากับ Java ใช้หน่วยความจำมีการติดตามความจำพื้นเมือง , pmap , jemalloc , async-Profiler

ปรับปรุง

นี่คือบันทึกของการนำเสนอของฉันเมมโมรี่ของกระบวนการ Java

ในวิดีโอนี้ฉันจะพูดถึงสิ่งที่อาจใช้หน่วยความจำในกระบวนการ Java วิธีตรวจสอบและควบคุมขนาดของพื้นที่หน่วยความจำบางพื้นที่และวิธีกำหนดโปรไฟล์การรั่วไหลของหน่วยความจำเนทีฟในแอปพลิเคชัน Java


1
ไม่ได้ถูกกักตัวไว้ในฮีปตั้งแต่ jdk7 หรือไม่? ( bugs.java.com/bugdatabase/view_bug.do?bug_id=6962931 ) - ฉันอาจจะคิดผิด
j-keck

5
@ j-keck ออบเจ็กต์ String อยู่ในฮีป แต่แฮชแท็ก (ที่เก็บข้อมูลและรายการที่มีการอ้างอิงและรหัสแฮช) อยู่ในหน่วยความจำนอกฮีป ฉันเรียบเรียงประโยคใหม่ให้แม่นยำยิ่งขึ้น ขอบคุณที่ชี้ให้เห็น
apangin

เพื่อเพิ่มสิ่งนี้แม้ว่าคุณจะใช้ ByteBuffers ที่ไม่ใช่โดยตรง JVM จะจัดสรรบัฟเฟอร์โดยตรงชั่วคราวในหน่วยความจำเนทีฟโดยไม่มีการ จำกัด หน่วยความจำ cf เลย evanjones.ca/java-bytebuffer-leak.html
Cpt. Senkfuss

17

https://developers.redhat.com/blog/2017/04/04/openjdk-and-containers/ :

เหตุใดเมื่อฉันระบุ -Xmx = 1g JVM ของฉันจึงใช้หน่วยความจำมากกว่า 1GB

การระบุ -Xmx = 1g เป็นการบอกให้ JVM จัดสรรฮีป 1gb ไม่ได้บอกให้ JVM จำกัด การใช้งานหน่วยความจำทั้งหมดไว้ที่ 1gb มีตารางการ์ดแคชโค้ดและโครงสร้างข้อมูลนอกฮีปอื่น ๆ ทุกประเภท พารามิเตอร์ที่คุณใช้เพื่อระบุการใช้หน่วยความจำทั้งหมดคือ -XX: MaxRAM โปรดทราบว่าด้วย -XX: MaxRam = 500m ฮีปของคุณจะมีขนาดประมาณ 250mb

Java เห็นขนาดหน่วยความจำของโฮสต์และไม่ทราบถึงข้อ จำกัด ของหน่วยความจำคอนเทนเนอร์ มันไม่ได้สร้างแรงกดดันของหน่วยความจำดังนั้น GC จึงไม่จำเป็นต้องปล่อยหน่วยความจำที่ใช้แล้ว ฉันหวังว่าXX:MaxRAMจะช่วยให้คุณลดความจำได้ ในที่สุดคุณสามารถปรับแต่งการตั้งค่า GC ( -XX:MinHeapFreeRatio, -XX:MaxHeapFreeRatio, ... )


เมตริกหน่วยความจำมีหลายประเภท ดูเหมือนว่านักเทียบท่าจะรายงานขนาดหน่วยความจำ RSS ซึ่งอาจแตกต่างจากหน่วยความจำ "คอมมิต" ที่รายงานโดยjcmd(Docker เวอร์ชันเก่ารายงานแคช RSS + เป็นการใช้หน่วยความจำ) การสนทนาและการเชื่อมโยงที่ดี: ความแตกต่างระหว่าง Resident Set Size (RSS) และ Java Total Commit Memory (NMT) สำหรับ JVM ที่ทำงานใน Docker container

หน่วยความจำ (RSS) สามารถกินได้โดยยูทิลิตี้อื่น ๆ ในคอนเทนเนอร์เช่นเชลล์ตัวจัดการกระบวนการ ... เราไม่รู้ว่ามีอะไรอีกบ้างที่ทำงานในคอนเทนเนอร์และคุณจะเริ่มกระบวนการในคอนเทนเนอร์ได้อย่างไร


-XX:MaxRamมันเป็นเรื่องจริงที่ดีขึ้นด้วย ฉันคิดว่ามันยังคงใช้มากกว่าค่าสูงสุดที่กำหนดไว้ แต่ก็ดีกว่าขอบคุณ!
Nicolas Henneaux

บางทีคุณอาจต้องการหน่วยความจำเพิ่มเติมสำหรับอินสแตนซ์ Java นี้ มี 15267 คลาส 56 เธรด
ม.ค. Garaj

1
นี่คือรายละเอียดเพิ่มเติมข้อโต้แย้ง Java -Xmx128m -Xms128m -Xss228k -XX:MaxRAM=256m -XX:+UseSerialGC, ผลิตและDocker 428.5MiB / 600MiB JVM ใช้พื้นที่ประมาณ 300MB ในขณะที่คอนเทนเนอร์ต้องการ 430MB 130MB ระหว่างการรายงาน JVM และการรายงาน OS อยู่ที่ไหน jcmd 58 VM.native_memory -> Native Memory Tracking: Total: reserved=1571296KB, committed=314316KB
Nicolas Henneaux

1
เพิ่มข้อมูล / ลิงค์เกี่ยวกับหน่วยความจำ RSS
ม.ค. Garaj

RSS ที่ให้มานั้นมาจากภายในคอนเทนเนอร์สำหรับกระบวนการ Java เท่านั้นที่ps -p 71 -o pcpu,rss,size,vsizeมีกระบวนการ Java ที่มี pid 71 อันที่จริง-XX:MaxRamไม่ได้ช่วยอะไร แต่ลิงก์ที่คุณให้ไว้ช่วยเกี่ยวกับ Serial GC
Nicolas Henneaux

8

TL; DR

รายละเอียดการใช้หน่วยความจำจัดทำโดยรายละเอียด Native Memory Tracking (NMT) (ส่วนใหญ่เป็นข้อมูลเมตาของรหัสและตัวรวบรวมขยะ) นอกจากนั้นคอมไพเลอร์ Java และเครื่องมือเพิ่มประสิทธิภาพ C1 / C2 ยังใช้หน่วยความจำที่ไม่ได้รายงานในสรุป

สามารถลดขนาดหน่วยความจำได้โดยใช้แฟล็ก JVM (แต่มีผลกระทบ)

การปรับขนาดคอนเทนเนอร์ Docker จะต้องทำผ่านการทดสอบด้วยการโหลดแอปพลิเคชันที่คาดไว้


รายละเอียดสำหรับแต่ละส่วนประกอบ

พื้นที่ชั้นที่ใช้ร่วมกันสามารถใช้งานในภาชนะตั้งแต่ชั้นเรียนจะไม่ถูกใช้ร่วมกันโดยกระบวนการ JVM อื่น สามารถใช้แฟล็กต่อไปนี้ มันจะลบพื้นที่ชั้นเรียนที่ใช้ร่วมกัน (17MB)

-Xshare:off

เก็บขยะอนุกรมมีรอยความทรงจำที่น้อยที่สุดที่ค่าใช้จ่ายของเวลาหยุดยาวระหว่างการประมวลผลการเก็บรวบรวมขยะ (ดูเปรียบเทียบ Aleksey Shipilëvระหว่าง GC ในหนึ่งภาพ ) สามารถเปิดใช้งานได้ด้วยแฟล็กต่อไปนี้ สามารถประหยัดพื้นที่ GC ที่ใช้ (48MB)

-XX:+UseSerialGC

คอมไพเลอร์ C2สามารถใช้งานกับสถานะต่อไปนี้เพื่อลดข้อมูล profiling ใช้ในการตัดสินใจว่าจะเพิ่มประสิทธิภาพหรือไม่วิธีการ

-XX:+TieredCompilation -XX:TieredStopAtLevel=1

พื้นที่โค้ดลดลง 20MB ยิ่งไปกว่านั้นหน่วยความจำภายนอก JVM จะลดลง 80MB (ความแตกต่างระหว่างพื้นที่ NMT และพื้นที่ RSS) คอมไพเลอร์การปรับให้เหมาะสม C2 ต้องการ 100MB

C1 และ C2 คอมไพเลอร์สามารถใช้งานกับธงดังต่อไปนี้

-Xint

ขณะนี้หน่วยความจำภายนอก JVM ต่ำกว่าพื้นที่คอมมิตทั้งหมด พื้นที่โค้ดลดลง 43MB ระวังสิ่งนี้มีผลกระทบอย่างมากต่อประสิทธิภาพของแอปพลิเคชัน การปิดใช้งานคอมไพเลอร์ C1 และ C2 จะลดหน่วยความจำที่ใช้ลง 170 MB

การใช้คอมไพเลอร์ Graal VM (การแทนที่ C2) จะทำให้หน่วยความจำมีขนาดเล็กลงเล็กน้อย เพิ่มพื้นที่หน่วยความจำโค้ด 20MB และลดลง 60MB จากหน่วยความจำภายนอก JVM

บทความJava Memory Management สำหรับ JVMให้ข้อมูลที่เกี่ยวข้องบางส่วนเกี่ยวกับพื้นที่หน่วยความจำที่แตกต่างกัน Oracle ให้รายละเอียดบางอย่างในเอกสารหน่วยความจำการติดตามพื้นเมือง รายละเอียดเพิ่มเติมเกี่ยวกับระดับการสะสมในนโยบายการสะสมขั้นสูงและในการปิดการใช้งาน C2 ลดขนาดแคชรหัสโดยปัจจัยที่ 5 รายละเอียดบางประการเกี่ยวกับเหตุใด JVM จึงรายงานหน่วยความจำที่มีการคอมมิตมากกว่าขนาดที่กำหนดในกระบวนการของ Linux เมื่อคอมไพเลอร์ทั้งสองถูกปิดใช้งาน


-1

Java ต้องการหน่วยความจำมาก JVM เองต้องการหน่วยความจำจำนวนมากในการเรียกใช้ ฮีปคือหน่วยความจำที่มีอยู่ในเครื่องเสมือนที่มีให้สำหรับแอปพลิเคชันของคุณ เนื่องจาก JVM เป็นบันเดิลขนาดใหญ่ที่อัดแน่นไปด้วยสารพัดประโยชน์จึงต้องใช้หน่วยความจำมากในการโหลด

เริ่มต้นด้วย java 9 คุณมีสิ่งที่เรียกว่าproject Jigsawซึ่งอาจลดหน่วยความจำที่ใช้เมื่อคุณเริ่มแอป java (พร้อมกับเวลาเริ่มต้น) จิ๊กซอว์โปรเจ็กต์และระบบโมดูลใหม่ไม่จำเป็นต้องสร้างขึ้นเพื่อลดหน่วยความจำที่จำเป็น แต่ถ้ามันสำคัญคุณสามารถลองได้

คุณสามารถดูตัวอย่างนี้: https://steveperkins.com/using-java-9-modularization-to-ship-zero-dependency-native-apps/ ด้วยการใช้ระบบโมดูลทำให้แอปพลิเคชัน CLI ขนาด 21MB (พร้อม JRE embeded) JRE ใช้พื้นที่มากกว่า 200mb สิ่งนี้ควรแปลเป็นหน่วยความจำที่จัดสรรน้อยลงเมื่อแอปพลิเคชันทำงาน (คลาส JRE ที่ไม่ได้ใช้จำนวนมากจะไม่ถูกโหลดอีกต่อไป)

นี่คือบทช่วยสอนที่ดีอีกอย่าง: https://www.baeldung.com/project-jigsaw-java-modularity

หากคุณไม่ต้องการใช้เวลากับสิ่งนี้คุณสามารถจัดสรรหน่วยความจำเพิ่มเติมได้ บางครั้งมันก็ดีที่สุด


การใช้งานjlinkค่อนข้าง จำกัด เนื่องจากต้องใช้แอปพลิเคชันเป็นโมดูลาร์ ไม่รองรับโมดูลอัตโนมัติจึงไม่มีวิธีง่ายๆในการไปที่นั่น
Nicolas Henneaux

-1

วิธีปรับขนาดขีด จำกัด หน่วยความจำ Docker ให้ถูกต้อง ตรวจสอบแอปพลิเคชันโดยการตรวจสอบเป็นระยะเวลาหนึ่ง หากต้องการ จำกัด หน่วยความจำของคอนเทนเนอร์ให้ลองใช้ตัวเลือก -m, --memory bytes สำหรับคำสั่ง docker run - หรือสิ่งที่เท่าเทียมกันหากคุณกำลังเรียกใช้เป็นอย่างอื่นเช่น

docker run -d --name my-container --memory 500m <iamge-name>

ไม่สามารถตอบคำถามอื่น ๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.