美文网首页
周一放送:如何解决JAVA运行在Docker上的问题

周一放送:如何解决JAVA运行在Docker上的问题

作者: 科学Jia | 来源:发表于2018-12-17 10:20 被阅读158次

    两个周末终于看完了<微服务设计>这本书,强烈推荐给像我这样的小白,它会让你在一个高度上去思考全局,不仅仅讲解了技术,也剖析了人性。
    “不管一开始看起来什么样,它永远是人的问题。请记住,如果没有把人们拉到一条船上,你想要的任何变化从一开始就注定会失败。”

    Java运行在Docker上的问题

    上周一个同事做压力测试,描述的测试现象是java吃内存很厉害,不一会儿就docker容器就crash重启。让我很自然的联想到了曾经看到过的一位oracle老师写的文章,它的题目大约是《java程序运行在docker容器上可能会有哪些问题?》。

    摘抄下当时记录下的笔记:

    Docker 仅在类似 Linux 内核之上实现了有限的隔离和虚拟化,并不是像传统虚拟化软件那样,独立运行在一个新的操作系统。容器虽然省略了虚拟操作系统的开销,实现了轻量级的目标,但也带来了额外的复杂度,它的限制对于应用不是透明的,需要用户理解docker的新行为。所以,有专家层级说过,“幸运的是Docker没有完全隐藏底层信息,但不幸的是也是Docker没有隐藏底层信息。”

    所以,对于Java平台而言,历史版本的Java显然并不能理解CGroup这种对资源的限制和隔离的技术。

    从JVM运行机制来看,例如:JVM会根据检测到的系统内存大小,启动默认的初始堆大小(1/64),堆的最大值为系统内存的1/4; 同时,它还会根据检查到的CPU个数直接设置Parallel GC的并行数目和JIT complier线程数。而这些参数的判断,很可能是错误信息做出的。印象很深的一句话是:“我以为我住的大别墅,结果实际我只有一个房间的使用权。”

    如何解决?

    1. 升级到最新的JDK版本,JDK9中引入了Docker和JVM进行资源沟通的参数设置:

    -XX:+UnlockExperimentalVMOptions
    -XX:+UseCGroupMemoryLimitForHeap

    注意这两个参数的顺序敏感,并且只支持linux环境。

    1. 如何升级到JDK10, 那么默认java会适应各种资源限制和实现差异。同时还增加了参数以明确指定CPU核心数目:

    -XX:ActiveProcessorCount=N

    1. 上面第一点提及到JDK9中的参数设置已经被移植到了Oracle JDK 8u131+

    如果JDK老版本使用Docker呢

    老师的建议是,明确设置堆,元数据等内存区域大小,保证JAVA进程的总大小可控。
    例如,

    1. 限制容器内存。
    2. 在dockerFile里,明确指定JVM堆大小。

    -e JAVA_OPTIONS='-Xmx300m'

    1. 明确配置GC和JIT并行线程数目,避免二者占用过多计算资源。

    -XX:ParallelGCThreads
    -XX:CICompilerCount

    1. 如果还是出现swap的现象,建议配置下面的参数,让JVM明确系统内存的限额

    -XX:MaxRAM=cat /sys/fs/cgroup/memory/memory.limit_in_bytes

    结论:平时还是要多读书啊

    后来在docker file里添加了对jvm的设置, 终于没有出现docker内存消耗并导致重启的现象了。

    ENV JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=2 -XshowSettings:vm"

    相关文章

      网友评论

          本文标题:周一放送:如何解决JAVA运行在Docker上的问题

          本文链接:https://www.haomeiwen.com/subject/xafqkqtx.html