你好,我是风一样的树懒,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。
前面我们学习了JVM中占用内存相关的基础知识:
《JVM:什么是直接内存(Direct Memory)有什么作用?》
现在我们来看这个问题,会不会轻松很多?
假设容器的内存总量为 4GB,而你不希望内存使用超过 3.6GB(即 90% 的内存限制),我们需要根据以下各个内存区域的需求来合理配置 JVM 参数:
堆内存(Heap Memory):JVM 的主要内存区域,用来存储对象。
元空间(Metaspace):存储类元数据、方法元数据等。
直接内存(Direct Memory):通过 NIO 或其他低级 API 使用的内存,不会在堆内存中分配。
线程栈内存(Thread Stack):每个线程有自己的栈空间。
为了避免堆内存过大导致容器超限,建议将最大堆内存设置为容器总内存的 60%-70%。这样可以避免 JVM 在最大堆内存使用时超出容器的限制。
例如,我们设置最大堆内存为 2.5GB,这样如果容器总内存为 4GB,堆内存最大使用 2.5GB 留给其他内存区域(元空间、直接内存、线程栈):
-Xms1g -Xmx2.5g
元空间的内存开销不在堆内存中,因此需要单独考虑。一般情况下,元空间的内存需求较小,通常设置为 200MB 到 300MB 即可。
-XX:MaxMetaspaceSize=300m
每个线程都有自己的栈内存。在高并发的情况下,线程栈内存可能会占用较大空间。如果容器中有大量线程,可以将线程栈内存调整为 较小的值。例如,设置每个线程栈的大小为 512KB(-Xss512k)。
-Xss512k
如果应用程序使用大量的直接内存(例如使用 NIO),则需要配置 -XX:MaxDirectMemorySize 来限制直接内存的最大使用量。如果没有明确需求,通常可以不设置,JVM 会动态调整直接内存的使用。如果有明确的直接内存需求,可以设置为 200MB。
-XX:MaxDirectMemorySize=200m
为了确保 JVM 能够识别容器内存限制,可以使用 -XX:+UseContainerSupport 让 JVM 更好地适应 Docker 容器环境。
-XX:+UseContainerSupport
java -XX:+UseContainerSupport -Xms1g -Xmx2.5g -XX:MaxMetaspaceSize=300m -Xss512k -XX:MaxDirectMemorySize=200m -jar myapp.jar
-Xms1g:初始堆内存为 1GB。
-Xmx2.5g:最大堆内存为 2.5GB,避免超过容器总内存的 70%。
-XX:MaxMetaspaceSize=300m:元空间最大为 300MB。
-Xss512k:每个线程栈的内存为 512KB,减少线程栈内存的占用。
-XX:MaxDirectMemorySize=200m:限制直接内存为 200MB。
-XX:+UseContainerSupport:确保 JVM 在 Docker 容器中运行时正确识别容器内存限制。
使用 docker stats 监控容器的内存使用情况,确保 Java 应用的内存使用没有超出容器的限制。
通过 Java 的 jcmd 或 jvisualvm 等工具进一步监控 JVM 的内存使用,尤其是堆内存、元空间和直接内存。
现在我们将这些内存占用进行加总,计算容器内内存的总使用量。
初始堆内存:1GB
最大堆内存:2.5GB
元空间:300MB
线程栈内存:50MB(假设100个线程× 512KB)
直接内存:200MB
最大内存占用:3.05GB,占用容器内存率约75%左右,线程可能会大于100,还需要保留部分容量。另外xms可以配置成xmx相同大小
通过这样的配置,你可以确保 JVM 不会超过 4GB 的容器内存限制,从而避免触发容器内存警告。
今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!