JAVA学习文档
垃圾回收机制
垃圾回收机制是干什么的?
- 在程序执行过程过程中,会出现有些对象空间被申请但是没有办法直接或者间接被GC Roots对象引用的情况,
我们称这类空间叫做垃圾空间,需要被清理掉,垃圾回收机制会通过如标记–清除算法(Mark-Sweep)、
标记–整理算法、复制算法等各类算法实现垃圾空间的清理。但是程序员没法直接清理某项数据,只能通过算法自动化清理。
可以作为GC Roots对象引用的情况
1、虚拟机栈(栈帧中的本地变量表)中引用的对象;
2、方法区中类静态属于引用的对象;
3、方法区中常量引用的对象;
4、本地方法栈中JNI(即一般说的Native方法)引用的对象。
垃圾回收机制是怎么实现的?
内存分类:
垃圾回收机制将Java中存放数据的堆分为了两个部分:
- 新生代(new):约占堆空间的1/3,存放一些容易被清理的数据
- 老年代(old):空间占比大约为1/2,存放一些不太容易被清理的数据
新生代和老年代划分依据
- 数据会首先分配到Eden区当中(当然也有特殊情况,如果是大对象那么会直接放入到
老年代(大对象是指需要大量连续内存空间的java对象)。当Eden没有足够空间的时候
就会触发jvm发起一次Minor GC,。如果对象经过一次Minor-GC还存活,并且又能被
Survivor空间接受,那么将被移动到Survivor空间当中。并将其年龄设为1,
对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,
就会被晋升到老年代中了,当然晋升老年代的年龄是可以设置的。
垃圾清理器Minor GC、Major GC、Full GC区别及触发条件:
Minor GC是新生代GC,指的是发生在新生代的垃圾收集动作。由于java对象大都是朝生夕死的,所以Minor GC非常频繁,一般回收速度也比较快。
触发条件:
1.eden区满时,触发MinorGC。即申请一个对象时,发现eden区不够用,则触发一次MinorGC。
2.新创建的对象大小 > Eden所剩空间
Major GC是老年代GC,指的是发生在老年代的GC,通常执行Major GC会连着Minor GC一起执行。Major GC的速度要比Minor GC慢的多。
触发条件:
1.每次晋升到老年代的对象平均大小>老年代剩余空间
2.MinorGC后存活的对象超过了老年代剩余空间
3.永久代(存放的是虚拟机中的方法区)空间不足
4.执行System.gc()
5.CMS GC异常
6.堆内存分配很大的对象
(注:元空间是metaspace,在jdk1.8的时候,jvm移除了永久代的概念,元空间也是对java虚拟机
的方法区的一种实现。元空间与永久代最大的区别在于,元空间不在虚拟机中,使用本地内存。)
Full GC是清理整个堆空间,包括年轻代和老年代
触发条件:基本同Major GC
新生代:
- 总体约占堆空间的1/3,其中存放的数据容易被清理掉,内部分为三个部分EdDen、From Survivor、To Survivor,占比为8:1:1,
- 垃圾清理器:Minor GC、Full GC
- 垃圾收集器:erial、ParNew、Parallel Scavenge、G1
- 一般适用垃圾回收算法:复制算法
老年代
- 总体约占堆空间的2/3,其中存放的数据不太容易被清理掉
- 垃圾清理器:Major GC、Full GC
- 垃圾收集器:CMS、Serial Old、Parallel Old、G1
- 一般适用垃圾回收算法:复制算法
垃圾收集器搭配
Serial / Serial Old
Serial / CMS
ParNew / Serial Old
ParNew / CMS
Parallel Scavenge / Serial Old
Parallel Scavenge / Parallel Old
G1
判断对象是否为垃圾(存活)
计数器法:
- 每个对象在创建的时候,就给这个对象绑定一个计数器。每当有一个引用指向该对象时,计数器加一;每当有一个指向它的引用被删除时,
计数器减一。这样,当没有引用指向该对象时,计数器为0就代表该对象死亡 - 优点:实现简单,判定效率也很高
- 缺点:无法判断互相引用的情况(即无法判断不包含GC Roots的环),如下
Test a = new Test();
Test b = new Test();
/**
* 循环引用,此时引用计数器法失效
*/
a.object = b;
b.object = a;
a = null;
b = null;
可达性分析法:
- 从GC Roots开始向下搜索,搜索所走过的路径为引用链。
当一个对象到GC Roots没用任何引用链时,则证明此对象是不可用的,表示可以回收。 -
- 优点:解决相互循环引用问题。
-
- 缺点:与计数器法相比无明显缺点
垃圾清理算法:
标记–清除算法(Mark-Sweep)
- 为每个对象存储一个标记位,记录对象的状态(活着或是死亡)。
分为两个阶段,一个是标记阶段,这个阶段内,为每个对象更新标记位,检查对象是否死亡;
第二个阶段是清除阶段,该阶段对死亡的对象进行清除,执行 GC 操作。 - 标记清除算法的优点:
1.是可以解决循环引用的问题
2.必要时才回收(内存不足时) - 标记清除算法的缺点:
1.回收时,应用需要挂起,也就是stop the world。
2.标记和清除的效率不高,尤其是要扫描的对象比较多的时候
3.会造成内存碎片(会导致明明有内存空间,但是由于不连续,申请稍微大一些的对象无法做到), - 标记清除算法的应用场景:该算法一般应用于老年代,因为老年代的对象生命周期比较长
标记–整理算法
- 标记清除算法和标记压缩算法非常相同,但是标记压缩算法在标记清除算法之上
解决内存碎片化(有些人叫"标记整理算法"为"标记压缩算法")标记-整理法是标记-清除法
的一个改进版。同样,在标记阶段,该算法也将所有对象标记为存活和死亡两种状态;
不同的是,在第二个阶段,该算法并没有直接对死亡的对象进行清理,
而是将所有存活的对象整理一下,放到另一处空间,然后把剩下的所有对象全部清除。
这样就达到了标记-整理的目的。 - 标记–整理算法优点:解决标记清除算法出现的内存碎片问题,
- 标记–整理算法缺点:压缩阶段,由于移动了可用对象,需要去更新引用。
- 标记–整理算法应用场景:该算法一般应用于老年代,因为老年代的对象生命周期比较长。
复制算法
-
该算法将内存平均分成两部分,然后每次只使用其中的一部分,
当这部分内存满的时候,将内存中所有存活的对象复制到另一个内存中,
然后将之前的内存清空,只使用这部分内存,循环下去。
这个算法与标记-整理算法的区别在于,该算法不是在同一个区域复制,
而是将所有存活的对象复制到另一个区域内。 -
复制算法的优点:在存活对象不多的情况下,性能高,能解决内存碎片和java垃圾回收算
法之-标记清除 中导致的引用更新问题。 -
复制算法的缺点:会造成一部分的内存浪费。不过可以根据实际情况,将内存块大小比例适当调整;如果存活对象的数量比较大,复制算法的性能会变得很差。
-
复制算法的应用场景:
1.复制算法一般是使用在新生代中,因为新生代中的对象一般都是朝生夕死的,存活对象的数量并不多,
这样使用复制算法进行拷贝时效率比较高。
2.jvm将Heap(堆)内存划分为新生代与老年代。又将新生代划分为Eden与2块Survivor Space(幸存者区) ,
然后在Eden –>Survivor Space 与To Survivor之间实行复制算法。
3.不过jvm在应用复制算法时,并不是把内存按照1:1来划分的,这样太浪费内存空间了。一般的jvm都是8:1。
也即是说,Eden区:From区:To区域的比例是始终有90%的空间是可以用来创建对象的,
而剩下的10%用来存放回收后存活的对象。
计数器法(仅了解)
- 每个对象在创建的时候,就给这个对象绑定一个计数器。每当有一个引用指向该对象时,计数器加一;每当有一个指向它的引用被删除时,
计数器减一。这样,当没有引用指向该对象时,计数器为0就代表该对象死亡。 - 优缺点同上。
垃圾收集器
垃圾回收器 | 工作区域 | 回收算法 | 工作线程 | 用户线程并行 | 描述 |
---|---|---|---|---|---|
Serial | 新生带 | 复制算法 | 单线程 | 否 | Client模式下默认新生代收集器。简单高效 |
ParNew | 新生带 | 复制算法 | 多线程 | 否 | Serial的多线程版本,Server模式下首选, 可搭配CMS的新生代收集器 |
Parallel Scavenge | 新生带 | 复制算法 | 多线程 | 否 | 目标是达到可控制的吞吐量 |
Serial Old | 老年带 | 标记-整理 | 单线程 | 否 | Serial老年代版本,给Client模式下的虚拟机使用 |
Parallel Old | 老年带 | 标记-整理 | 多线程 | 否 | Parallel Scavenge老年代版本,吞吐量优先 |
CMS | 老年带 | 标记-清楚 | 多线程 | 是 | 追求最短回收停顿时间 |
G1 | 新生带 + 老年带 | 标记-整理 + 复制算法 | 多线程 | 是 | JDK1.9以上默认垃圾收集器 |
G1
- G1 收集器:分代收集器。当今收集器技术发展最前沿成果之一,是一款面向服务端应用的垃圾收集器。G1可以说是CMS的终极改进版,解决了CMS内存碎片、更多的内存空间登问题。虽然流程与CMS比较相似,但底层的原理已是完全不同。
- 特点:
1.能充分利用多CPU、多核环境下的硬件优势;
2.可以并行来缩短(Stop The World)停顿时间;
3.也可以并发让垃圾收集与用户程序同时进行;
4.分代收集,收集范围包括新生代和老年代
5.能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;
6.能够采用不同方式处理不同时期的对象;
7.应用场景可以面向服务端应用,针对具有大内存、多处理器的机器;
8.采用标记-整理 + 复制算法来回收垃圾
//使用方法:
设置垃圾收集器:"-XX:+UseG1GC":指定使用G1收集器;
设置垃圾收集器参数:"-XX:InitiatingHeapOccupancyPercent":当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45;
设置垃圾收集器参数:"-XX:MaxGCPauseMillis":为G1设置暂停时间目标,默认值为200毫秒;
设置垃圾收集器参数:"-XX:G1HeapRegionSize":设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region