| tags: [ Java ] categories: [ Development ]
Java Memory Optimization
Progress on JDK
-
JDK 10: http://openjdk.java.net/projects/jdk/10/
- http://openjdk.java.net/jeps/310 Application Class-Data Sharing
# Determining the classes to archive java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=hello.lst \ -cp hello.jar HelloWorld # Creating the AppCDS archive java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=hello.lst \ -XX:SharedArchiveFile=hello.jsa -cp hello.jar # Verify classpath mistach and mmap java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=hello.jsa \ -cp hello.jar -Xlog:class+load=info HelloWorld # Production deployment java -Xshare:auto -XX:+UseAppCDS -XX:SharedArchiveFile=hello.jsa \ -cp hello.jar HelloWorld
- http://openjdk.java.net/jeps/310 Application Class-Data Sharing
-
JDK 11: http://openjdk.java.net/projects/jdk/11/
-
http://openjdk.java.net/jeps/318 Epsilon: A No-Op Garbage Collector
java -XX:+UseEpsilonGC
-
http://openjdk.java.net/jeps/333 ZGC
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
不支持 ClassUnloading 和 ClassUnloadingWithConcurrentMark 选项,不支持 Graal,在 JDK 12 中解决。
-
-
JDK 12: http://openjdk.java.net/projects/jdk/12/
-
http://openjdk.java.net/jeps/189 Shenandoah: A Low-Pause-Time Garbage Collector
java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
-XX:ShenandoahUncommitDelay=MILLISECONDS
归还内存,默认五分钟。 -
http://openjdk.java.net/jeps/341 Default CDS Archives 64-bit JDK 构建时自动对系统类执行 -Xshare:dump,在 JDK 11 中 -Xshare:auto 是默认选项,所以用户可以自动使用 CDS。
-
http://openjdk.java.net/jeps/346 Promptly Return Unused Comitted Memory from G1
java -XX:G1PeriodicGCInterval=300000 -XX:G1PeriodicGCSystemLoadThreshold=LOAD \ -XX:-G1PeriodicGCInvokesConcurrent -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=30
关掉 G1PeriodicGCInvokesConcurrent 会触发 Full GC 以归还更多内存,但对应用干扰更大。注意 G1PeriodicGCInterval 默认是 0。
-
-
JDK 13: https://openjdk.java.net/projects/jdk/13/
-
http://openjdk.java.net/jeps/350 Dynamic CDS Archives 简化 AppCDS 的生成,并且依赖 JDK 默认的 CDS archive
java -XX:ArchiveClassesAtExit=hello.jsa -cp hello.jar Hello java -XX:SharedArchiveFile=hello.jsa -cp hello.jar Hello
-
http://openjdk.java.net/jeps/351 ZGC: Uncommit Unused Memory
-XX:ZUncommitDelay=SECONDS
启用,需要-Xms
小于-Xmx
,只支持 Linux >= 3.5 or >= 4.3。
-
-
JDK 14: https://openjdk.java.net/projects/jdk/14/
-
https://openjdk.java.net/jeps/345 NUMA-Aware Memory Allocation for G1 使用
+XX:+UseNUMA
打开。 -
ZGC on macOS and Windows
-
Remove CMS
-
通常 ZGC 在吞吐量和暂停时间方面比 Shenandoah 更好,但是 Shenandoah 在极端情况比如堆接近耗尽、突发的高速内存分配时表现更好,而且 ZGC 在设计上要求 64bit 指针,因此不能使用 compressed oops,意味着在小于 32G 的 Java heap 上比较浪费内存,读操作由于 CPU 缓存缘故略慢,好处是写操作略快,因为不需要额外处理 compressed oop。
Tuning Recommendation
For JDK 13.
JAVA_OPTS="-server \
-XX:+UnlockDiagnosticVMOptions \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseShenandoahGC \
-XX:+UseLargePages \
-XX:+UseNUMA \
-Xms64m \
-Xmx256m \
-XX:MinHeapFreeRatio=10 \
-XX:MaxHeapFreeRatio=30 \
-XX:+ClassUnloadingWithConcurrentMark \
-XX:+ExplicitGCInvokesConcurrent \
-XX:+ExitOnOutOfMemoryError \
-Xlog:gc*,gc+age=trace,safepoint:file=log/gc.log:time,pid,level,tags:filecount=10,filesize=50m \
-Xbatch \
-XX:+PrintCommandLineFlags \
-XX:+PrintFlagsFinal \
--show-version \
-Djdk.nio.maxCachedBufferSize=65536 \
-Djava.awt.headless=true \
-Duser.timezone=Asia/Shanghai \
-Dfile.encoding=UTF-8"
# For Debug
#JAVA_OPTS="$JAVA_OPTS -XX:NativeMemoryTracking=detail -XX:+PrintNMTStatisticsec"
# build time
java -XX:ArchiveClassesAtExit=hello.jsa -cp hello.jar Hello
# run time
java -XX:SharedArchiveFile=hello.jsa -cp hello.jar Hello
Debug off-heap memory usage
-
-XX:+UnlockDiagnosticVMOptions
打开后可以使用 jcmd PID GC.class_stats -
-XX:NativeMemoryTracking=summary|detail
打开后可以使用 jcmd PID VM.native_memory,-XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatisticsec
打开后 JVM 会在退出时输出 native memory 信息 -
pmap -x PID
查看 /proc/PID/smaps 信息 -
jcmd 7 VM.native_memory summary scale=MB
,输出分为如下几类,其中占用内存最多的是 Class, Thread, Code, Internal:- Java Heap;
- Class,meta space,包含类的元信息以及 String.intern 的字符串
- Thread,thread stack,每个线程占用 1MB stack
- Code,code cache JIT 生成的机器码
- GC,GC 算法所需的内部数据结构
- Internal,包含 DirectByteBuffer 分配的 off-heap memory(新版本 JDK放到了 Other 类)
- Symbol
- Unknown
-
没有实现
hashCode()
和equals()
方法的对象,作为 HashSet 和 HashMap 的 key 时会发生内存泄漏 -
HeapByteBuffer 使用 DirectByteBuffer 来拷贝数据,为了避免频繁分配内存,每个线程都会缓存一些 DirectByteBuffer,使用
-Djdk.nio.maxCachedBufferSize=65536
(bytes)来限制缓存,超过此大小的 DirectByteBuffer 会在拷贝完数据后立刻释放。 -
-XX:MaxDirectMemorySize=xx
默认跟-Xmx
参数的值相同,当 Java heap 非常大或者非常小时,可以通过这个参数调节 DirectByteBuffer 可以使用的最大内存。 -
在 Java heap 富裕时,很少触发 full gc,可能导致 old region 引用的 DirectByteBuffer 不能尽早释放,可以尝试定期 full GC。
-
glibc < 2.12 的线程局部内存分配区可能保留太多内存:64MB * num_cores * MALLOC_ARENA_MAX(默认为 8),可以使用 pmap 检查 64MB 的内存段数量,并通过
export MALLOC_ARENA_MAX=${MALLOC_ARENA_MAX:-4}
来缓解,或者换用 jemalloc、tcmalloc、mimalloc。
References
- https://docs.oracle.com/en/java/javase/13/gctuning/index.html
- https://dzone.com/articles/troubleshooting-problems-with-native-off-heap-memo
- https://docs.oracle.com/en/java/javase/13/vm/native-memory-tracking.html#GUID-710CAEA1-7C6D-4D80-AB0C-B0958E329407
- https://docs.oracle.com/en/java/javase/13/troubleshoot/diagnostic-tools.html#GUID-1F53A50E-86FF-491D-A023-8EC4F1D1AC77
- https://ionutbalosin.com/2019/12/jvm-garbage-collectors-benchmarks-report-19-12/
- https://ionutbalosin.com/2020/01/hotspot-jvm-performance-tuning-guidelines/
- https://chriswhocodes.com/ VM Options Explorer
- https://www.youtube.com/watch?v=MU8NapbG1IQ Understanding Low Latency JVM GCs by Jean Philippe Bempel
- https://www.slideshare.net/JeanPhilippeBEMPEL/understanding-low-latency-jvm-gcs
- https://github.com/GregBowyer/ManagedRuntimeInitiative/tree/master/MRI-J/hotspot/src/azshare/vm/gc_implementation/genPauseless C4(之前叫GPGC) 2010 年开源的代码
- https://www.infoq.com/articles/azul_gc_in_detail/
- https://shipilev.net/talks/jugbb-Sep2019-shenandoah.pdf
- http://mail.openjdk.java.net/pipermail/zgc-dev/2017-December/000012.html C4, ZGC, Shenandoah 2.0 使用的 multi-mapping 会导致 Linux 内核重复计算 RSS