栈:
栈中的生命周期是跟随线程,所以不是关注的重点
堆:
堆中的对象是共享的,也是存活时间比较长的,所以是重点。
新生代采用是复制回收算法,老年代采用的标记清除,标记整理算法。
复制算法(
Copying
)
就是将from区和to区分成1:1,然后当发生新生代GC
(
Minor GC/Young GC两者叫法不一样,其实意思都是年轻代的GC)的时候,会把from区的数据复制到to区去,然后格式化from区的对象。再发生Minor GC/Young的时候重新把to区的对象又复制到from区去,然后格式化to区的对象,这个就是复制回收算法,所以这就是为什么from和to是1:1的原因。
这个算法的特点如下:
1,因为要留一半空间回收,导致空间利用率只有一半。
2,内存分配时也就不用考虑内存碎片等复杂情况,实现简单,运行高效
Appel
式回收
这是专门针对Eden区回收的一种方式,
新生代中的对象
98%
是“朝生夕死”的,所以整个年轻代并不需要按照
1:1
的比例来划分内存空间,这样会很浪费空间,
因为对象首先都是在这个区域产生的,10%的内存会被“浪费”。所以
HotSpot
虚拟机默认
Eden
和
from还有to区
的大小比例是
8:1:1.
Appel
式回收具体的实现方式就是当发生GC的时候第一次会把Eden区的对象丢到from区,第二次GC的时候把from区和Eden区的对象放到to区,第三次就是把Eden区和to区的对象重新放到from区,依次反复,当对象年龄等于15的时候直接到老年代了,如果对象很大,from或者to区放不下的话也放到老年代。
标记
-
清除算法(
Mark-Sweep
)
顾名思义就是标记+清除两个阶段,他的特点就是尽量会进行扫描标记两次,然后清除但是会产生大量的内存碎片,
标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连 续内存而不得不提前触发另一次垃圾回收动作。
标记
-
整理算法(
Mark-Compact
)
顾名思义就是标记+整理两个阶段,,他的特点就是尽量会进行扫描标记两次,然后整理清除,内存不会产生碎片,但是整理后再清除效率会很低,还会移动对象的内存地址,
直接指针需要调整。
JVM
中常见的垃圾回收器
Serial/Serial Old
JVM
刚诞生就只有这种,最古老的,单线程,独占式,成熟,适合单
CPU
,一般用在客户端模式下。
这种垃圾回收器只适合几十兆到一两百兆的堆空间进行垃圾回收(可以控制停顿时间再
100ms
左右),但是对于超过这个大小的内存回收速度很慢。
Parallel Scavenge
(
ParallerGC
)
/Parallel Old
为了提高回收效率,从
JDK1.3
开始,
JVM
使用了多线程的垃圾回收机制,关注吞吐量的垃圾收集器,高吞吐量则可以高效率地利用
CPU
时间,尽快完成
程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
所谓吞吐量就是
CPU
用于运行用户代码的时间与
CPU
总消耗时间的比值,即吞吐量
=
运行用户代码时间
/
(运行用户代码时间
+
垃圾收集时间),虚拟机总
共运行了
100
分钟,其中垃圾收集花掉
1
分钟,那吞吐量就是
99%
。
该垃圾回收器适合回收堆空间 上百兆
~
几个
G
JDK1.8
默认就是以下组合
-XX:+UseParallelGC
新生代使用
Parallel Scavenge
,老年代使用
Parallel Old
Concurrent Mark Sweep
(
CMS
)
1 初始标记,STW的时间很短,因为
仅仅只是标记一下
GC Roots
能直接关联到的对象,速度很快
2 并发标记,这个时候就会去GC Roots根节点下面找要回收的对象,和其他用户线程并行,不会STW,但是时间很长
3 重新标记,为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短
4 并发清理,由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,从总体上来说,CMS 收集器的内存回收过程是与用
户线程一起并发执行的。
-XX:+UseConcMarkSweepGC
,表示新生代使用
ParNew
,老年代的用
CMS
总结特点
1,CMS
对处理器资源敏感,毕竟采用了并发的收集、当处理核心数不足
4
个时,
CMS
对用户的影响较大。
2,浮动垃圾:当并发清理的时候不暂停用户线程,清理的过程中用户线程又可能产生垃圾,这个就叫浮动垃圾,只能等下一次GC的时候才会清理了。
由于浮动垃圾的存在,因此需要预留出一部分内存,意味着
CMS
收集不能像其它收集器那样等待老年代快满的时候再回收。
在
1.6
的版本中老年代空间使用率阈值
(92%)
如果预留的内存不够存放浮动垃圾,就会出现
Concurrent Mode Failure
,这时虚拟机将临时启用
Serial Old
来替代
CMS
。
3,会产生空间碎片
CMS 采用了标记清除算法,所以会有内存碎片,
当碎片较多时,给大对象的分配带来很大的麻烦,为了解决这个问题,
CMS
提供一个
参数:
-XX:+UseCMSCompactAtFullCollection
,一般是开启的,如果分配不了大对象,就进行内存碎片的整理过程。
这个地方一般会使用
Serial Old
,因为
Serial Old
是一个单线程,所以如果内存空间很大、且对象较多时
,CMS
发生这样情况会很卡。
Garbage First(G1)
设计思想
随着
JVM
中内存的增大,
STW
的时间成为
JVM
急迫解决的问题,但是如果按照传统的分代模型,总跳不出
STW
时间不可预测这点。
为了实现
STW
的时间可预测,首先要有一个思想上的改变。
G1
将堆内存“化整为零”,将堆内存划分成多个大小相等独立区域(
Region
),每一个
Region
都可以根据需要,扮演新生代的
Eden
空间、
Survivor
空间,或者老年代空间。回收器能够对扮演不同角色的
Region
采用不同的策略去处理,这样无论是
新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果
Region
Region
可能是
Eden,
也有可能是
Survivor,
也有可能是
Old,
另外
Region
中还有一类特殊的
Humongous
区域,专门用来存储大对象。
G1
认为只要大小超过
了一个
Region
容量一半的对象即可判定为大对象。每个
Region
的大小可以通过参数
-XX:G1HeapRegionSize
设定,取值范围为
1MB~32MB,
且应为
2
的
N
次
幂。而对于那些超过了整个
Region
容量的超级大对象,将会被存放在
N
个连续的
Humongous Region
之中,
G1
的进行回收大多数情况下都把
Humongous
Region
作为老年代的一部分来进行看待。
参数设置
开启参数
-XX:+UseG1GC
分区大小
-XX:+G1HeapRegionSize
一般建议逐渐增大该值,随着
size
增加,垃圾的存活时间更长,
GC
间隔更长,但每次
GC
的时间也会更长。
1,初始标记 和cms一样,短暂的暂停线程,
仅仅只是标记一下
GC Roots
能直接关联到的对象,并且修改
TAMS
指针的值,让下一阶段用户线程并发运行时,能正确地在可用的
Region
中分配新对象。
这个阶段需要停顿线程,但耗时很短
TAMS
是什么?
要达到
GC
与用户线程并发运行,必须要解决回收过程中新对象的分配,所以
G1
为每一个
Region
区域设计了两个名为
TAMS
(
Top at Mark Start
)的指针,
从
Region
区域划出一部分空间用于记录并发回收过程中的新对象。这样的对象认为它们是存活的,不纳入垃圾回收范围。
2,并发标记 和cms一样,和用户线程一起
当对象图扫
描 完 成 以 后 , 并 发 时 有 引 用 变 动 的 对 象 , 这 些 对 象 会 漏 标 , 漏 标 的 对 象 会 被 一 个 叫 做
SATB(snapshot-at-the-beginning)算法来解决
3,
最终标记
( Final Marking)
对用户线程做另一个短暂的暂停,用于处理并发阶段结后仍遗留下来的最后那少量的
SATB
记录
(
漏标对象
)
。
4,
筛选回收
( Live Data Counting and Evacuation)
负责更新
Region
的统计数据,对各个
Region
的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个
Region
构
成回收集,然后把决定回收的那一部分
Region
的存活对象复制到空的
Region
中,再清理掉整个旧
Region
的全部空间。这里的操作涉及存活对象的移动,
是必须暂停用户线程,由多条收集器线程并行完成的
特点
并行与并发
:
G1
能充分利用多
CPU
、多核环境下的硬件优势,使用多个
CPU
(
CPU
或者
CPU
核心)来缩短
Stop-The-World
停顿的时间,部分其他收集器
原本需要停顿
Java
线程执行的
GC
动作,
G1
收集器仍然可以通过并发的方式让
Java
程序继续执行。
分代收集
:与其他收集器一样,分代概念在
G1
中依然得以保留。虽然
G1
可以不需要其他收集器配合就能独立管理整个
GC
堆,但它能够采用不同的方式
去处理新创建的对象和已经存活了一段时间、熬过多次
GC
的旧对象以获取更好的收集效果。
空间整合
:与
CMS
的“标记—清理”算法不同,
G1
从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个
Region
之间)上来看是基于“复
制”算法实现的,但无论如何,这两种算法都意味着
G1
运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运
行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次
GC
。
追求停顿时间:
-XX:MaxGCPauseMillis
指定目标的最大停顿时间,
G1
尝试调整新生代和老年代的比例,堆大小,晋升年龄来达到这个目标时间。
怎么玩?
该垃圾回收器适合回收堆空间上百
G
。一般在
G1
和
CMS
中间选择的话平衡点在
6~8G
,只有内存比较大
G1
才能发挥优势。