GC-垃圾回收机制

一.垃圾回收机制干嘛的

当我们运行一个程序时,创建的对象和一些变量是存在内存中,如果我们创建的对象和变量过多,它会占用大量的内存,在程序运行时,有一些对象和变量可能是无用的,我们没必要浪费内存去存储,垃圾回收机制就是帮我们回收这些无用的垃圾。

二.垃圾回收机制主要针对哪些内存

JVM运行时内存区域主要包括五大部分,分别是程序计数器,本地方法栈,java虚拟栈,方法区,堆,由于,程序计数器,本地方法栈,java虚拟栈是每个线程私有的,当线程运行完毕时,会自动回收这些内存区域,而对于堆和方法区是线程共享的,这部分内存的分配和回收都是动态的,所以方法区和堆是GC主要针对的区域。堆中需要回收的主要是对象,方法区回收的主要是一些无用的常量和类,无用的常量可以通过引用计数可以判断是否是废弃的常量。无用的类是指该类的实例都已经被回收,加载该类的ClassLoader已经被回收,该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类

三.GC搜索算法

引用计数器:

​ 每一个对象都拥有一个对象引用计数器,当增加一个对该对象的引用时,引用计数器就会加一,减少一个对象引用,引用计数器就会减一,当该对象的引用计数器为0时,则认为该对象没有被引用是可以进行回收的,但是引用计数器有一个缺点,就是无法解决循环引用,比如A对象引用了B对象,B对象引用了A对象,但是这两个对象没有被任何的其它对象引用,这两个对象就无法回收了

GC Roots可达性分析

从一些GC ROOTS对象作为起点,向下搜索,搜索通过的路径为引用链,当一个对象没有被该引用链连接时,则认为该对象是无用的。

GC Roots对象包括虚拟机栈中的引用的对象,方法区域中的类静态属性引用的对象,方法区域中常量引用的对象。

四.对象的引用是什么

无论是引用计数器还是可达性分析,判断对象是否有用都与引用有关,那如何定义对象的引用。

Java中对象的引用分为四种级别,由高到低分别为:强引用,软引用,弱引用,虚引用

强引用(Strong Reference):

强引用在我们每天写代码的时候都会用到,比如Object obj = new Object();如果一个对象被强引用引用,那么这个对象是不会被垃圾回收器回收的。

软引用(SoftReference):

如果一个对象具有软引用,当JVM内存空间充足的情况下,垃圾回收器不会回收它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Object obj = new Object();
// 软引用
SoftReference<Object> sr = new SoftReference<>(obj);
try {
System.out.println(obj);//java.lang.Object@15db9742
System.out.println(sr.get());//java.lang.Object@15db9742
obj = null;
// 创建超出最大堆内存的对象
byte []bs = new byte[1024*1024*10];
} catch (Exception e) {
}finally {
System.out.println(obj); //null
System.out.println(sr.get());//null 堆内存不够,清理软引用
}

可以用来实现内存敏感的高速缓存.在jvm报告内存不足时,立刻清空所有软引用

gc回收软引用的过程

  1. 首先将SoftReference引用的obj置空
  2. 标记new Object()为finalize
  3. 回收内存,并添加到RefererceQueue.(如果有的话)

弱引用

被弱引用引用的对象可有可无,只要被GC扫描到,随时都会被清除。

1
2
3
4
5
6
7
8
Object obj = new Object();
WeakReference<Object> wr = new WeakReference<Object>(obj);
System.out.println(obj);//java.lang.Object@15db9742
System.out.println(wr.get());//java.lang.Object@15db9742
obj = null;
System.gc();
System.out.println(obj); //null
System.out.println(wr.get());//null 虚引用 gc回收垃圾时回收

虚引用

如果一个对象持有虚引用,那么它就与没有任何引用一样,在任何时候都会被垃圾回收器回收,不能单独使用也不能通过它访问任何对象,虚引用必须和引用队列(ReferenceQueue)联合使用

1
2
3
4
5
6
7
8
9
10
11
12
Object obj = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> pr = new PhantomReference<Object>(obj,queue);
System.out.println(obj);//java.lang.Object@15db9742
System.out.println(pr.get());//null
System.out.println(queue.poll());//null
//=================================
obj = null;
System.gc();
System.out.println(obj);//null
System.out.println(pr.get());//null
System.out.println(queue.poll());//java.lang.ref.PhantomReference@6d06d69c

五.GC回收算法

  • 标记清除法:标记清除法从GC ROOT出发,进行扫描,对存活的对象进行标记,标记完后,再扫描整个空间未被标记的对象,进行回收。标记清除法容易产生大量不连续的空间。
  • 标记整理法:采用与标记清除法一样的方式对对象进行标记,它会将存活的对象移到一端,然后将边界的对象清除,解决了内存碎片的问题。
  • 复制算法:把堆分成大小相同的两块,每次使用一块,将存活的对象移动到空闲的一块,然后将另一块的内存清空。由于需要分配空闲的内存,所以内存利用率不高
  • 分代收集算法:大部分jvm目前采用的算法,它将根据对象存活的生命周期将内存分为若干不同的区域。一般分为老年代,新生代,在堆区之外还分配一个永久代。老年代的特点是每次垃圾回收只有少量的对象需要被回收,一般采用标记整理法,新生代由于每次都有大量的对象需要被回收,一般采用复制算法。

新生代内存按照8:1:1的比例分为一个eden区,和两个survivor区(survivor0,survivor1),所有新生成的对象首先会被放在年轻代,回收时先将一个eden区的存活对象复制到一个survivor0区,然后清空eden区,当survivor0被放满了,会将eden区和survivor0区的对象复制到survivor1,然后清空eden和这个survivor0区,此时survivor0区是空的,然后将survivor0与survivor1交换,保证survivor1是空的,如此循环往复。但是当survivor1不足以存放eden区和survivor0区的对象时,就将存活对象存放到老年代,若是老年代也存满了则触发Full GC,就是新生代老年代都进行回收。当新对象在Eden区申请空间失败时,会触发Eden区的垃圾回收。(Scavenge GC)

在年轻代存活了N次垃圾回收后任然存活的对象,就会被存放到老年代,当老年代内存满的时候会触发Full GC.(对整个堆进行垃圾回收)

文章目录
  1. 1. 一.垃圾回收机制干嘛的
  2. 2. 二.垃圾回收机制主要针对哪些内存
  3. 3. 三.GC搜索算法
    1. 3.0.1. 引用计数器:
    2. 3.0.2. GC Roots可达性分析
  • 4. 四.对象的引用是什么
    1. 4.0.1. 强引用(Strong Reference):
    2. 4.0.2. 软引用(SoftReference):
    3. 4.0.3. 弱引用
    4. 4.0.4. 虚引用
  • 5. 五.GC回收算法
  • |
    载入天数...载入时分秒...