• Ukieweb

    佳的博客

    曾梦想仗剑天涯,后来工作忙没去。

java 程序 jvm 垃圾回收算法 收集器 和 内存溢出

1.垃圾回收算法(GC,Garbage Collection)

红色是标记的非活动对象绿色活动对象

  • 标记-清除(Mark-Sweep)

GC 分为两个阶段,标记清除。首先标记所有可回收的对象,在标记完成后统一回收所有被标记的对象。同时会产生不连续的内存碎片

碎片过多会导致以后程序运行时需要分配较大对象时无法找到足够的连续内存,而不得已再次触发GC

image.png

  • 复制(Copy)

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

image.png

  • 标记-整理(Mark-Compact)

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

image.png

年轻代 和 老年代 收集方法:

一般年轻代中执行 GC 后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以完成收集

老年代中因为对象存活率高,没有额外过多内存空间分配,就需要使用标记-清理或者标记-整理算法来进行回收。

2. 垃圾收集器

  • 串行收集器(Serial)

比较老的收集器,单线程。收集时,必须暂停应用的工作线程,直到收集结束。

  • 并行收集器(Parallel)

条垃圾收集线程并行工作,在多核CPU下效率更高,必须暂停应用的工作线程,直到收集结束。

  • CMS 收集器(Concurrent Mark Sweep)

CMS 收集器是缩短暂停应用时间为目标而设计的,是基于标记-清除算法实现。

整个过程分为 4 个步骤,包括:

  1. 初始标记(Initial Mark)

  2. 并发标记(Concurrent Mark)

  3. 重新标记(Remark)

  4. 并发清除(Concurrent Sweep)

其中,初始标记重新标记这两个步骤仍然需要暂停应用线程

  • 初始标记只是标记一下GC Roots 能直接关联到的对象速度很快

  • 并发标记阶段是标记可回收对象

  • 重新标记阶段则是为了修正并发标记期间因用户程序继续运作导致标记产生变动的那一部分对象的标记记录,这个阶段暂停时间比初始标记阶段一点,但远比并发标记时间短

由于整个过程中消耗最长并发标记并发清除过程收集器线程都可以与用户线程一起工作,所以大大减少了暂停时间

  • 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 暂停时间来执行回收

3.垃圾收集器参数

image.png

4. 为什么会堆内存溢出?

年轻代中经过 GC 后还存活的对象会被复制到老年代中。当老年代空间不足时,JVM会对老年代进行完全的垃圾回收(Full GC)。如果 GC 后,还是无法存放从 Survivor 区复制过来的对象,就会出现OOM(Out of Memory)

OOM(Out of Memory)异常常见有以下几个原因:

  • 老年代内存不足:java.lang.OutOfMemoryError:Javaheapspace

  • 永久代内存不足:java.lang.OutOfMemoryError:PermGenspace

  • 代码bug,占用内存无法及时回收。

OOM 在这几个内存区都有可能出现,实际遇到 OOM 时,能根据异常信息定位到哪个区的内存溢出。

可以通过添加个参数 -XX:+HeapDumpOnOutMemoryError,让虚拟机在出现内存溢出异常时 Dump 出当前的内存堆转储快照以便事后分析。

5. 参数设置例子

熟悉了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 日志,方便后期分析






0
0
下一篇:jenkins pipeline 给 变量 parameters 或使用 三元运算符 给 环境变量 赋初值

0 条评论

老佳啊

85后,大专学历,中原人士,家里没矿。

由于年轻时长的比较帅气,导致在别人眼里,我一直不谈恋爱的原因是清高,实则是自己的小自卑。最大的人生目标就是找一个相知相爱相容的人,共度余生。

和人相处时如果能感受到真诚,会非常注重彼此的关系,对别人没有什么心机,即使有利益冲突,一般也会以和为贵,因为在这个世界上,物质的东西,从来不会吸引到我。

特别迷恋那些大山大水,如果现在还能隐居,可能早就去了。对那些宏伟的有底蕴的人文景观比较不感冒。

从事于IT行业,却一直对厨房念念不忘,由于身材魁梧,总觉得自己上辈子是个将军,可惜这辈子没当兵,也不会打架。