虚拟机就好比是一个有限空间的一个房子,在我们生活中,也会产生各种各样的垃圾,虚拟机也不例外,垃圾满了会造成内存溢出等问题,那虚拟机是怎么进行垃圾回收的呢?让我们来揭开这神秘的面纱
1.概述
程序计数器、虚拟机栈、本地方法栈都是随着线程而生,随着线程而灭,当方法结束时或者是线程结束时,内存会自动进行回收。但是堆和方法区则不同,他们具有不确定性,比如在线程运行的时候,我们并不确定会创建多少个对象,多少个方法,每个对象或者方法会被分配多少内存,一切的一切都是未知因数,故为动态。
2.“死亡”还是“生存”?
下来我们来介绍两种判断对象是否死亡的方法
1.引用计数算法
但是有一种情况不方便。就是两个对象互相引用循环,以至于计数器无法回收
2.可达性分析
通过一个GC Roots的根对象作为起始点,向下搜索,路径称为引用链,如果对象和这个GC Roots没有任何的引用链连接,就会被判定为可回收对象
GC Roots的对象包括以下几种:
- 在虚拟机栈(栈帧中的本地变量表)中引用的对象(方法堆栈中使用到的参数、局部变量、临时变量等)
- 在方法区中类静态属性引用的对象(java类的引用类型静态变量)
- 在方法区中常量引用的对象(字符串常量池中的引用)
- 在本地方法栈中(Native方法)引用的对象
- java虚拟机内部引用(基本类型对应的class对象,部分异常对象)、系统类加载器
- 所有被同步锁持有的对象
- 本地代码缓存
- 区域不同,其他临时性的对象
3.引用
一共分为四种引用:强引用、软引用、弱引用、虚引用
- 强引用:在程序代码中普遍存在的引用赋值
Odject object=new Objet();
- 软引用:有用且非必要的对象,在内存溢出时会进行二次回收
- 弱引用:非必要对象,只能生存到下一次垃圾收集为止
- 虚引用:无实在性的作用,唯一目的只是在这个对象被收集时收到一个系统通知
4.死亡前的最后一根稻草
在以前在刑场上经常会有关键的救命文书,在虚拟机里也有类似的情况,被可达性算法判定为不可达对象,对象只是判为“缓刑”,而不是真正的立即处死,它真正的判断死亡需要2次标记,具体看下图
5.回收方法区
方法区回收主要是两部分:
1.废弃的常量
2.不在使用的类型
那如何判断一个常量是否废弃呢?下面3个条件至关重要
- 该类的所有实例均被回收
- 加载该类的加载器已被回收
- 该类的java.lang.class对象没有在任何地方进行引用