java8JVM堆内存(heap)新⽣代⽼年代元空间垃圾回收详解JAVA堆内存管理是影响性能主要因素之⼀。堆内存溢出是JAVA项⽬⾮常常见的故障,在解决该问题之前,必须先了解下JAVA堆内存是怎么⼯作的。先看下JAVA堆内存是如何划分的,如图:
JVM内存划分为堆内存和⾮堆内存,堆内存分为年轻代(Young Generation)、⽼年代(Old Generation),⾮堆内存就⼀个永久代(Permanent Generation)。
年轻代⼜分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占⼤容量,Survivor两个区占⼩容量,默认⽐例是8:1:1。
堆内存⽤途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
⾮堆内存⽤途:永久代,也称为⽅法区,存储程序运⾏时长期存活的对象,⽐如类的元数据、⽅法、常量、属性等。
在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是⽅法区的实现,他们最⼤区别是:元空间并不在JVM中,⽽是使⽤本地内存。
元空间有注意有两个参数:
MetaspaceSize :初始化元空间⼤⼩,控制发⽣GC阈值
MaxMetaspaceSize :限制元空间⼤⼩上限,防⽌异常占⽤过多物理内存
为什么移除永久代?
移除永久代原因:为融合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频率。针对分类进⾏不同的垃圾回收算法,对算法扬长避短。为什么survivor分为两块相等⼤⼩的幸存空间?
主要为了解决碎⽚化。如果内存碎⽚化严重,也就是两个对象占⽤不连续的内存,已有的连续内存不够新对象存放,就会触发GC。
JVM堆内存常⽤参数
参数描述
-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,Garbage Collection)
红⾊是标记的⾮活动对象,绿⾊是活动对象。
标记-清除(Mark-Sweep)GC分为两个阶段,标记和清除。⾸先标记所有可回收的对象,在标记完成后统⼀回收所有被标记的对象。同时会产⽣不连续的内存碎⽚。碎⽚过多会导致以后程序运⾏时需要分配较⼤对象时,⽆法到⾜够的连续内存,⽽不得已再次触发GC。
复制(Copy)
将内存按容量划分为两块,每次只使⽤其中⼀块。当这⼀块内存⽤完了,就将存活的对象复制到另⼀块上,然后再把已使⽤的内存空间⼀次清理掉。这样使得每次都是对半个内存区回收,也不⽤考虑内存碎⽚问题,简单⾼效。缺点需要两倍的内存空间。
标记-整理(Mark-Compact)也分为两个阶段,⾸先标记可回收的对象,再将存活的对象都向⼀端移动,然后清理掉边界以外的内存。此⽅法避免标记-清除算法的碎⽚问题,同时也避免了复制算法的空间问题。⼀般年轻代中执⾏GC后,会有少量的对象存活,就会选⽤复制算法,只要付出少量的存活对象复制成本就可以完成收集。⽽⽼年代中因为对象存活率⾼,没有额外过多内存空间分配,就需要使⽤标记-清理或者标记-整理算法来进⾏回收。
垃圾收集器
串⾏收集器(Serial)
⽐较⽼的收集器,单线程。收集时,必须暂停应⽤的⼯作线程,直到收集结束。
并⾏收集器(Parallel)
多条垃圾收集线程并⾏⼯作,在多核CPU下效率更⾼,应⽤线程仍然处于等待状态。
CMS收集器(Concurrent Mark Sweep)
    CMS收集器是缩短暂停应⽤时间为⽬标⽽设计的,是基于标记-清除算法实现,整个过程分为4个步骤,包括:
初始标记(Initial Mark)
jvm调优参数并发标记(Concurrent Mark)
重新标记(Remark)
并发清除(Concurrent Sweep)
其中,初始标记、重新标记这两个步骤仍然需要暂停应⽤线程。初始标记只是标记⼀下GC Roots能直接关联到的对象,速度很快,并发标记阶段是标记可回收对象,⽽重新标记阶段则是为了修正并发标记期间因⽤户程序继续运作导致标记产⽣变动的那⼀部分对象的标记记录,这个阶段暂停时间⽐初始标记阶段稍长⼀点,但远⽐并发标记时间段。
由于整个过程中消耗最长的并发标记和并发清除过程收集器线程都可以与⽤户线程⼀起⼯作,所以,CMS收集器内存回收与⽤户⼀起并发执⾏的,⼤⼤减少了暂停时间。
G1收集器(Garbage First)G1收集器将堆内存划分多个⼤⼩相等的独⽴区域(Region),并且能预测
暂停时间,能预测原因它能避免对整个堆进⾏全区收集。G1跟踪各个Region⾥的垃圾堆积价值⼤⼩(所获得空间⼤⼩以及回收所需时间),在后台维护⼀个优先列表,每次根据允许的收集时间,优先回收价值最⼤的Region,从⽽保证了再有限时间内获得更⾼的收集效率。
G1收集器⼯作⼯程分为4个步骤,包括:
初始标记(Initial Mark)
并发标记(Concurrent Mark)
最终标记(Final Mark)
筛选回收(Live Data Counting and Evacuation)
初始标记与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"
设置堆内存最⼩和最⼤值,最⼤值参考历史利⽤率设置
设置GC垃圾收集器为G1
启⽤GC⽇志,⽅便后期分析

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。