垃圾回收概述和相关算法
1、什么是垃圾
- 垃圾是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的对象。
- 如果不及时对内存中的垃圾进行清理,那么这些垃圾对象所占的内存空间会保存在内存中,直到应用程序结束,被保留的空间无法被其他对象使用,就有可能导致内存溢出
2、Java垃圾回收机制
1、哪些区域回收垃圾
-
垃圾收集器可以对年轻代回收,也可以对老年代回收,甚至是全栈和方法区的回收,
-
其中,Java堆是垃圾收集器的工作重点
-
从次数上讲:
-
频繁收集Young区
-
较少收集Old区
-
基本不收集Perm区(元空间)
-
3、垃圾回收相关算法
1、标记阶段
`主要是为了判断对象是否存活,判断对象存活有两种方式:引用计数法和可达性分析算法`
1.引用计数法
概述
对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1;当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收。
优点
实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性。
缺点
- 它需要单独的字段存储计数器,这样的做法增加了存储空间的开销
- 致命的缺点:循环引用,Java不能解决这个缺点,就没有使用引用计数法
2、可达性分析算法
概述(GCRoots)
- 可达性分析算法是以根对象集合(GCRoots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。
- 在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象。
GCRoots可以是哪些元素
- 虚拟机栈中引用的对象
- 本地方法栈内JNI(通常说的本地方法)引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 所有被同步锁synchronized持有的对象
清除阶段
1、标记-清除算法
执行过程
- 标记:Collector从引用根节点开始遍历,标记所有被引用的对象。一般是在对象的Header中记录为可达对象。
- 注意:标记的是被引用的对象,也就是可达对象,并非标记的是即将被清除的垃圾对象
- 清除:Collector对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收
标记-清除算法的优缺点
- 标记清除算法的效率不算高
- 在进行GC的时候,需要停止整个应用程序,用户体验较差
- 这种方式清理出来的空闲内存是不连续的,产生内碎片,需要维护一个空闲列表
2、复制算法(年轻代)
执行过程
- 将年轻代内存空间中分为伊甸园区、from和to区
- 在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收
复制算法的优缺点
- 没有标记和清除过程,实现简单,运行高效
- 复制过去以后保证空间的连续性,不会出现“碎片”问题。
- 此算法的缺点也是很明显的,就是需要两倍的内存空间。
3、标记-整理算法(老年代)
执行过程
- 第一阶段和标记清除算法一样,从根节点开始标记所有被引用对象
- 第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。之后,清理边界外所有的空间。
标记-整理算法的优缺点
- 消除了标记-清除算法当中,内存区域分散的缺点,我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可。
- 从效率上来说,标记-整理算法要低于复制算法(需要遍历两次内存数据,标记遍历一次,整理遍历一次)。
- 移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址(因为HotSpot虚拟机采用的不是句柄池的方式,而是直接指针)
- 移动过程中,需要全程暂停用户应用程序。即:STW