JAVA堆内存管理是影响性能主要因素之一。
堆内存溢出是JAVA项目很是常见的故障,在解决该问题以前,必须先了解下JAVA堆内存是怎么工做的。java
先看下JAVA堆内存是如何划分的,如图:算法
在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上相似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。
元空间有注意有两个参数:架构
移除永久代缘由:为融合HotSpot JVM与JRockit VM(新JVM技术)而作出的改变,由于JRockit没有永久代。
有了元空间就再也不会出现永久代OOM问题了!并发
新生成的对象首先放到年轻代Eden区,当Eden空间满了,触发Minor GC,存活下来的对象移动到Survivor0区,Survivor0区满后触发执行Minor GC,Survivor0区存活对象移动到Suvivor1区,这样保证了一段时间内总有一个survivor区为空。通过屡次Minor GC仍然存活的对象移动到老年代。
老年代存储长期存活的对象,占满时会触发Major GC=Full GC,GC期间会中止全部线程等待GC完成,因此对响应要求高的应用尽可能减小发生Major GC,避免响应超时。
Minor GC : 清理年轻代
Major GC : 清理老年代
Full GC : 清理整个堆空间,包括年轻代和永久代
全部GC都会中止应用全部线程。运维
将对象根据存活几率进行分类,对存活时间长的对象,放到固定区,从而减小扫描垃圾时间及GC频率。针对分类进行不一样的垃圾回收算法,对算法扬长避短。ide
主要为了解决碎片化。若是内存碎片化严重,也就是两个对象占用不连续的内存,已有的连续内存不够新对象存放,就会触发GC。微服务
参数 | 描述 |
---|---|
-Xms | 堆内存初始大小,单位m、g |
-Xmx(MaxHeapSize) | 堆内存最大容许大小,通常不要大于物理内存的80% |
-XX:PermSize | 非堆内存初始大小,通常应用设置初始化200m,最大1024m就够了 |
-XX:MaxPermSize | 非堆内存最大容许大小 |
-XX:NewSize(-Xns) | 年轻代内存初始大小 |
-XX:MaxNewSize(-Xmn) | 年轻代内存最大容许大小,也能够缩写 |
-XX:SurvivorRatio=8 | 年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1 |
-Xss | 堆栈内存大小 |
红色是标记的非活动对象,绿色是活动对象。性能
其中,初始标记、从新标记这两个步骤仍然须要暂停应用线程。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段是标记可回收对象,而从新标记阶段则是为了修正并发标记期间因用户程序继续运做致使标记产生变更的那一部分对象的标记记录,这个阶段暂停时间比初始标记阶段稍长一点,但远比并发标记时间段。
因为整个过程当中消耗最长的并发标记和并发清除过程收集器线程均可以与用户线程一块儿工做,因此,CMS收集器内存回收与用户一块儿并发执行的,大大减小了暂停时间。spa
初始标记与CMS同样,标记一下GC Roots能直接关联到的对象。并发标记从GC Root开始标记存活对象,这个阶段耗时比较长,但也能够与应用线程并发执行。而最终标记也是为了修正在并发标记期间因用户程序继续运做而致使标记产生变化的那一部分标记记录。最后在筛选回收阶段对各个Region回收价值和成本进行排序,根据用户所指望的GC暂停时间来执行回收。线程
参数 | 描述 |
---|---|
-XX:+UseSerialGC | 串行收集器 |
-XX:+UseParallelGC | 并行收集器 |
-XX:+UseParallelGCThreads=8 | 并行收集器线程数,同时有多少个线程进行垃圾回收,通常与CPU数量相等 |
-XX:+UseParallelOldGC | 指定老年代为并行收集 |
-XX:+UseConcMarkSweepGC | CMS收集器(并发收集器) |
-XX:+UseCMSCompactAtFullCollection | 开启内存空间压缩和整理,防止过多内存碎片 |
-XX:CMSFullGCsBeforeCompaction=0 | 表示多少次Full GC后开始压缩和整理,0表示每次Full GC后当即执行压缩和整理 |
-XX:CMSInitiatingOccupancyFraction=80% | 表示老年代内存空间使用80%时开始执行CMS收集,防止过多的Full GC |
-XX:+UseG1GC | G1收集器 |
-XX:MaxTenuringThreshold=0 | 在年轻代通过几回GC后还存活,就进入老年代,0表示直接进入老年代 |
在年轻代中通过GC后还存活的对象会被复制到老年代中。当老年代空间不足时,JVM会对老年代进行彻底的垃圾回收(Full GC)。若是GC后,仍是没法存放从Survivor区复制过来的对象,就会出现OOM(Out of Memory)。
OOM(Out of Memory)异经常见有如下几个缘由:
1)老年代内存不足:java.lang.OutOfMemoryError:Javaheapspace
2)永久代内存不足:java.lang.OutOfMemoryError:PermGenspace
3)代码bug,占用内存没法及时回收。
OOM在这几个内存区都有可能出现,实际遇到OOM时,能根据异常信息定位到哪一个区的内存溢出。
能够经过添加个参数-XX:+HeapDumpOnOutMemoryError,让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便过后分析。
熟悉了JAVA内存管理机制及配置参数,下面是对JAVA应用启动选项调优配置:
JAVA_OPTS="-server -Xms512m -Xmx2g -XX:+UseG1GC -XX:SurvivorRatio=6 -XX:MaxGCPauseMillis=400 -XX:G1ReservePercent=15 -XX:ParallelGCThreads=4 -XX: ConcGCThreads=1 -XX:InitiatingHeapOccupancyPercent=40 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:../logs/gc.log"
在2018/2019年Docker/Kubernetes容器技术无疑是业内最火的技术。根据招聘简介状况来看,容器技术已成为运维工程师、架构师必备技能。
为帮助你们快速掌握这门主流技术,少走弯路,提升核心竞争力。决定写《基于Kubernetes企业容器云平台落地与实践》文章专栏,给朋友在企业落地容器云平台提供一些企业实践性指导,但愿本身所学所思的东西可以帮助到你们,可以有所启发。