乐趣区

关于java:jvm堆内存和GC简介

最近常常遇到 jvm 内存问题,感觉还是有必要整顿下 jvm 内存的相干逻辑,这里只形容 jvm 堆内存,对外内存暂不论述。

jvm 内存简图

  1. jvm 内存分为堆内存和非堆内存,堆内存分为年老代、老年代,非堆内存里只有个永恒代。
  2. 年老代分为生成区(Eden)和幸存区(Survivor),幸存区由 FromSpace 和 Tospace 两局部组成,默认状况下,内存大小比例:Eden:FromSpace:ToSpace 为 8:1:1。
  3. 堆内存寄存的是对象,垃圾收集器回收的就是这里的对象,不同区域的对象依据不同的 GC 算法回收,比方年老代对应 Minor GC,老年代对应 Major GC。
  4. 非堆内存即永恒代,也称为办法区,存储的是程序运行时长期存活的对象,比方类的元数据、办法、常量、属性等。
  5. 在 jdk1.8 废除了永恒代,应用元空间(MetaSpace)取而代之,元空间存储的对象与永恒代雷同,区别是:元空间并不在 jvm 中,应用的是本地内存。

    为什么移除永恒代呢

为交融 HotSpot JVM 与 JRockit VM(新 JVM 技术)而做出的扭转,因为 JRockit 没有永恒代。

分代概念

首先,GC 是 Garbage Collection,即垃圾回收。

新生成的对象首先寄存在生成区,当生成区满了,触发 Minor GC,存活下来的对象转移到 Survivor0,即 FromSpace,Survivor0 区满后触发执行 Minor GC,存活对象挪动到 Suvivor1 区,即 ToSpace,通过屡次 Minor GC 依然存活的对象转移到老年代。

所以老年代存储的是长期流动的对象,当老年代满了会触发 Major GC。

Minor GC 和 Major GC 是俗称,有些状况下 Major GC 和 Full GC 是等价的,如果登程了 Full GC, 那么所有线程必须期待 GC 实现能力持续(见 GC 分类和算法)。

分代起因

将对象依据存活概率进行分类,对存活工夫长的对象,放到固定区,从而缩小扫描垃圾工夫及 GC 频率。针对分类进行不同的垃圾回收算法,对算法取长补短。

为什么幸存辨别为大小雷同的两局部:S0,S1

次要为了解决碎片化,因为回收一部分对象后,残余对象占用的内存不间断,也就是碎片化,过于重大的话,以后间断的内存不够新对象寄存就会触发 GC,这样会进步 GC 的次数,升高性能,当 S0 GC 后存活对象转移到 S1 后存活对象占用的就是间断的内存。

GC 分类和相干算法

咱们来看下 GC 分类,能力分明什么时候触发 Full GC、和非 Full GC,GC 大抵分为两种:

  • Partial GC:并不收集整个 GC 堆的模式,即能够了解为非 Full GC

    • Young GC:只收集 young gen 的 GC
    • Old GC:只收集 old gen 的 GC。只有 CMS 有这个模式
    • Mixed GC:收集整个 young gen 以及局部 old gen 的 GC。只有 G1 有这个模式
  • Full GC:收集整个堆,包含 young gen、old gen、perm gen(如果存在的话)等所有局部的模式。

下面说的 CMS 和 G1 都是 GC 的算法,相干 GC 算法如下:

  1. Serial GC 算法:Serial Young GC + Serial Old GC(实际上它是全局范畴的 Full GC);
  2. Parallel GC 算法:Parallel Young GC + 非并行的 PS MarkSweep GC / 并行的 Parallel Old GC(这俩实际上也是全局范畴的 Full GC),选 PS MarkSweep GC 还是 Parallel Old GC 由参数 UseParallelOldGC 来管制;
  3. CMS 算法:ParNew(Young)GC + CMS(Old)GC + Full GC for CMS 算法;
  4. G1 GC 算法:Young GC + mixed GC(新生代,再加上局部老生代)+ Full GC for G1 GC 算法(应答 G1 GC 算法某些时候的不赶趟,开销很大);

GC 触发条件

  • Young GC:各种 Young GC 触发的条件都是 Eden 区满了。
  • Serial Old GC/PS MarkSweep GC/Parallel Old GC:当筹备要触发一次 young GC 时,如果发现统计数据说之前 young GC 的均匀降职大小比目前 old gen 残余的空间大,则不会触发 young GC 而是转为触发 full GC。
  • Full GC for CMS:老年代应用比率超过某个值。
  • Full GC for G1 GC:Heap 应用比率超过某个值。
  • 如果有 perm gen 的话,要在 perm gen 调配空间但曾经没有足够空间时,也要触发一次 full GC。

    小结:不同算法对应的 GC 回收条件是不同的。

    GC 形式

    标记 - 革除(Mark-Sweep)

    GC 分为两个阶段,标记和革除。首先标记所有可回收的对象,在标记实现后对立回收所有被标记的对象。同时会产生不间断的内存碎片,碎片过多会导致当前程序运行时须要调配较大对象时,无奈找到足够的间断内存,而不得已再次触发 GC。

    红色代表被标记的可回收对象,绿色代表存活对象

    革除后如下:

    复制(Copy)

    将内存按容量划分为两块,每次只应用其中一块。当这一块内存用完了,就将存活的对象复制到另一块上,而后再把已应用的内存空间一次清理掉。这样使得每次都是对半个内存区回收,也不必思考内存碎片问题,简略高效。毛病须要两倍的内存空间。

革除前:

革除后:

标记 - 整顿(Mark-Compact)

也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端挪动,而后清理掉边界以外的内存。此方 法防止标记 - 革除算法的碎片问题,同时也防止了复制算法的空间问题。

个别年老代中执行 GC 后,会有大量的对象存活,就会选用复制算法,只有付出大量的存活对象复制老本就能够 实现收集。而老年代中因为对象存活率高,没有额定过多内存空间调配,就须要应用标记 - 清理或者标记 - 整顿算法来 进行回收。

革除前:

革除后:

附 GC 算法参数

参数 形容
-XX:+UseSerialGC SerialGC 收集器
-XX:+UseParallelGC ParallelGC 收集器
-XX:+UseParallelGCThreads=8 ParallelGC 收集器线程数,同时有多少个线程进行垃圾回收,个别与 CPU 数量相等
-XX:+UseParallelOldGC ParallelGC 收集器,指定老年代为并行收集
-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 示意间接进入老年代

先到这里吧,思否公布之后不能再次编辑,我感觉能够思考改良下。

退出移动版