深入理解JVM之GC
1.GC详解
1.GC 的作用域
口诀:关于垃圾回收:分代收集算法
不同的区域使用不同的算法
Young代: GC频繁区域
Old代:GC次数较少
Perm代:不会产生GC!
2. 一个对象的历程!
JVM在进行GC时,并非每次都是对三个区域进行扫描的!大部分的时候都是指的新生代!
3.GC两个类型:
-
普通GC:只针对新生代 【GC】
-
全局GC:主要是针对老年代,偶尔伴随新生代! 【Full GC】
4. GC 面试题
1、JVM内存模型,每个区中存放什么?
2、堆中的分区:Eden、S form to、老年代,请你说说他的特点?
3、GC的三种收集方法:标记清除、标记整理、复制算法,请你谈谈他的特点?
2.GC四大算法
1.引用计数法
特点:
- 每个对象都有一个引用计数器,每当对象被引用一次,计数器就+1,如果引用失效,则计数器-1,如果为0,则GC可以清理;
缺点:
- 计数器维护麻烦!
- 循环引用无法处理!
JVM 一般不采用这种方式
现在一般使用可达性算法,GC Root…
2.复制算法
年轻代中,就是使用的复制算法!
1、一般普通GC 之后,差不多Eden几乎都是空的了!
2、每次存活的对象,都会被从 from 区和 Eden区等复制到 to区,from 和 to 会发生一次交换;记住一个点就好,谁空谁是to,每当幸存一次,就会导致这个对象的年龄+1;如果这个年龄值大于15(默认值,后面我们会讲解调整),就会进入养老区!
优点:
- 没有标记和清除的过程!效率高!没有内存碎片!
缺点:
- 需要浪费双倍的空间
Eden 区,对象存活率极低! 统计:99% 对象都会在使用一次之后,引用失效!推荐使用 复制算法
3.标记清除算法
老年代一般使用这个,但是会和我们后面的整理压缩一起使用!
优点:
- 不需要额外的空间!
缺点:
- 两次扫描,耗时较为严重,会产生内存碎片,不连续!
4.标记清除压缩!
减少了上面标记清除的缺点:没有内存碎片!但是耗时可能也较为严重!
那我们什么时候可以考虑使用这个算法呢?
在我们这个要使用算法的空间中,假设这个空间中很少,不经常发生GC,那么可以考虑使用这个算法!
5.GC算法小结
-
内存效率:复制算法 > 标记清除算法 > 标记整理(时间复杂度!)
-
内存整齐度:复制算法=标记整理>标记清除算法
-
内存利用率:标记整理 = 标记清除算法 > 复制算法
从效率来说,复制算法最好,但是空间浪费较多!为了兼顾所有指标,标记整理会平滑一点,但是效率不尽人意!
难道就没有一种最优的算法吗?思考一下:
答案:没有!没有最好的,只有最合适的!=> 分代收集算法:不同的区域使用不同的算法!
年轻代:
相对于老年区,对象存活率低!
Eden 区,对象存活率极低! 统计:99% 对象都会在使用一次之后,引用失效!推荐使用 复制算法
老年代:
区域比较大,对象存活率较高!
推荐使用:标记清除压缩!
3.GC Roots(JVM 垃圾回收的时候如何确定垃圾)
什么是垃圾:简单地说,就是不在被引用的对象!
Person person = null;
如果我们要进行垃圾回收,第一步:判断这个对象是否可以回收!
1.引用计数法!
Java中,引用和对象都是有关联的,如果要操作对象,就要通过引用进行;
2.可达性分析算法!
一切都是从 GC Root 这个对象开始遍历的,只要在这里面的就不是垃圾!
3. 什么是Gc Root,(4种)
1、虚拟机栈中引用的对象!
2、类中静态属性引用的对象
3、方法区中的常量
4、本地方法栈中 Native 方法引用的对象!
public class GCRoots{
// private byte[] array = new byte[100*1024*1024]; // 开辟内空间!
// private static GCRoots2 t2; // GC root;
// private static final GCRoots3 t3 = new GCRoots3(); // GC root;
// 引用远远不止于此,强引用,软引用,弱引用,虚引用! 四个类的使用!
public static void m1(){
GCRoots g1 = new GCRoots(); //GCroot
System.gc();
}
public static void main(String[] args){
m1();
}
}
4.GC回收日志详解
1.GC回收触发代码
package com.coding.oom;
import java.util.Random;
/*
* -Xmx8m -Xms8m -XX:+PrintGCDetails
* GC :串行执行 STW(Stop The World) 并行执行 G1
*/
public class Demo02 {
public static void main(String[] args) {
System.gc(); // 手动唤醒GC(),等待cpu的调用
String str = "ilovecoding";
while (true){
str += str
+ new Random().nextInt(999999999)
+ new Random().nextInt(999999999);
}
// 出现问题:java.lang.OutOfMemoryError: Java heap space
}
}
2.GC日志详情分析
[GC (System.gc()) [PSYoungGen: 615K->496K(2048K)] 835K->740K(7680K), 0.0010787 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 496K->0K(2048K)] [ParOldGen: 244K->622K(5632K)] 740K->622K(7680K), [Metaspace: 3141K->3141K(1056768K)], 0.0061742 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
* 分析GC日志:
*
* [Times: user=0.00 sys=0.00, real=0.00 secs]
* 1、GC 类型 GC:普通的GC,Full GC :重GC
* 2、1536K 执行 GC之前的大小
* 3、504K 执行 GC之后的大小
* 4、(2048K) young 的total大小
* 5、0.0012643 secs 清理的时间
* 6、user 总计GC所占用CPU的时间 sys OS调用等待的时间 real 应用暂停的时间
*
* GC :串行执行 STW(Stop The World) 并行执行 G1
*/