美文网首页
周一放送:如何解决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