我不理解啊宝,为啥Java应用迁移到容器后会出现OOM啊


JVM启动后默认将最大使用堆大小设置为物理内存的四分之一,譬如一台普通的x86服务器配置128G内存,那么启动在容器的内启动JVM会将自己最大允许使用的堆内存调整为32G内存,如果容器启动时设置JVM只允许使用4G大小的内存,那么当JVM使用内存超过4G后,将会导致内核杀死JVM。测试代码如下:

import JAVA.util.ArrayList; 
import JAVA.util.List; 
 
public class MemEat { 
    public static void main(String[] args) { 
        List l = new ArrayList<>(); 
        while (true) { 
            byte b[] = new byte[1048576]; 
            l.add(b); 
            Runtime rt = Runtime.getRuntime(); 
            System.out.println( "free memory: " + rt.freeMemory() ); 
        } 
    } 
} 

代码非常简单,只是通过一个死循环不停地申请内存,如果是在JAVA 8u111版本之前,直接通过docker run -m 100m限制使用100M内存的情况下,运行一段时间后直接被内核杀死。输出如下:

# JAVA MemEat 
. . . 
free memory: 1307309488 
free memory: 1306260896 
free memory: 1305212304 
free memory: 1304163712 
free memory: 1303115120 
Killed 

为了避免这种情况,可以通过“ -Xmx ”设置最大堆内存后再次运行

# JAVA -Xmx100m MemEat 
. . . 
free memory: 8382264 
free memory: 7333672 
free memory: 6285080 
free memory: 5236488 
Exception in thread "main" JAVA.lang.OutOfMemoryError: JAVA heap space MemEat.main(MemEat.JAVA:8)

可以看到JVM由于堆内存不足,自己退出了。这种在JVM添加参数的方式有个弊端:如果修改了容器的内存限制,还需要调整启动参数。为此在JAVA 8u144版本之后添加了动态调整的功能,能够根据用户设定的内存限制动态调整,启动参数如下:

# JAVA -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap MemEat 

当我们修改了内存参数后JVM便可以随之调整。JAVA对于容器的支持不断增强到最新的JAVA 10版本后,已经原生支持容器环境,无需添加任何参数。不仅如此,新版JAVA 10还支持CPU在容器内动态调整。如下所示JVM调整内存最大堆:

# docker  run -it -m 1024M --entrypoint bash openjdk:11-jdk    
# java -XX:+PrintFlagsFinal -version | grep MaxHeapSize 
  size_t MaxHeapSize = 268435456 

可以看到上面的最大堆调整到内存限制的四分之一,而非物理内存的四分之一。还可以支持CPU自适应,如下所示:

# docker  run -it --CPUs 2 ---entrypoint bash openjdk:11-jdk 
jshell> Runtime.getRuntime().availableProcessors() 
$1 ==> 2 

可以看到通过JAVA的API成功地获取到当前设置的CPU个数。

如果是其他编程语言希望获取到容器的CPU和内存限制,可以通过容器内的cgroup文件系统,如获取容器内存的限制:

# cat /sys/fs/cgroup/memory/memory.limit_in_bytes 
104857600 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java 程序可能出现 OOM(Out of Memory)的情况有以下几种: 1. 堆内存不足:Java 程序的堆内存用于存储对象实例,如果堆内存不足,就出现 OOM 问题。例如,Java 程序中创建了大量的对象实例,但是堆内存不足以存储这些对象实例,就出现 OOM 问题。 2. 元空间不足:Java 8 引入了元空间(Metaspace)来代替 PermGen,用于存储类元数据。如果元空间不足,就出现 OOM 问题。例如,Java 程序中动态生成了大量的类,但是元空间不足以存储这些类的元数据,就出现 OOM 问题。 3. 栈空间不足:Java 程序的栈空间用于存储方法调用的状态和局部变量等信息。如果栈空间不足,就出现 StackOverflowError(栈溢出)问题。例如,Java 程序中递归调用了一个方法,但是栈空间不足以存储递归调用的状态,就出现 StackOverflowError 问题。 4. 本地内存不足:Java 程序可以使用本地方法库来调用本地系统资源,例如调用 C 语言编写的库文件。如果本地内存不足,就出现 OOM 问题。例如,Java 程序中调用了大量的本地方法,但是本地内存不足以存储这些方法的数据和状态,就出现 OOM 问题。 5. 永久代(PermGen)不足:Java 7 及之前的版本使用 PermGen 来存储类元数据和字符串常量等信息。如果 PermGen 不足,就出现 OOM 问题。例如,Java 程序中动态生成了大量的类或者字符串常量,但是 PermGen 不足以存储这些信息,就出现 OOM 问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值