虚拟机的垃圾收集(一)

       虚拟机就好比是一个有限空间的一个房子,在我们生活中,也会产生各种各样的垃圾(没有被任何引用指向的对象),虚拟机也不例外,垃圾满了会造成内存溢出等问题,那虚拟机是怎么进行垃圾回收的呢?让我们来揭开这神秘的面纱

 

1.概述

程序计数器、虚拟机栈、本地方法栈都是随着线程而生,随着线程而灭,当方法结束时或者是线程结束时,内存会自动进行回收。但是堆和方法区则不同,他们具有不确定性,比如在线程运行的时候,我们并不确定会创建多少个对象,多少个方法,每个对象或者方法会被分配多少内存,一切的一切都是未知因数,故为动态。

2.内存溢出和内存泄露

内存溢出:经过垃圾回收后,内存中仍然无法存储新创建的对象,内存不够而导致的溢出

内存泄漏:一些已经不用的对象,但是垃圾回收不能判断为垃圾,这些对象默默的占用的内存,称为内存泄露,大量的此类对象的存在,也是导致内存溢出的原因。

3.“死亡”还是“生存”?

下来我们来介绍两种判断对象是否死亡的方法

1.引用计数算法

String s1=new String("abc");//+1
String s2=s1;//+1
s2=null;//-1
s1=null;//-1

       但是有一种情况不方便。就是两个对象互相引用循环,以至于计数器都不为0,不能回收,产生内存泄露。

2.可达性分析

通过一个GC Roots的根对象作为起始点,向下搜索,路径称为引用链,如果对象和这个GC Roots没有任何的引用链连接,就会被判定为可回收对象

GC Roots的对象包括以下几种:

  • 在虚拟机栈(栈帧中的本地变量表)中引用的对象(方法堆栈中使用到的参数、局部变量、临时变量等)
  • 在方法区中类静态属性引用的对象(java类的引用类型静态变量)
  • 在方法区中常量引用的对象(字符串常量池中的引用)
  • 在本地方法栈中(Native方法)引用的对象
  • java虚拟机内部引用(基本类型对应的class对象,部分异常对象)、系统类加载器
  • 所有被同步锁持有的对象
  • 本地代码缓存
  • 区域不同,其他临时性的对象

 4.引用

一共分为四种引用强引用、软引用、弱引用、虚引用

强引用的对象是不能被垃圾进行回收的,软引用、弱引用、虚引用是来标记对象的一种状态(已经是垃圾了)

  • 强引用:在程序代码中普遍存在的引用赋值
Odject object=new Objet();
  • 软引用:有用且非必要的对象,在内存溢出时会进行二次回收(如果在内存充足的情况下,可以保留软引用对象,如果内存不足,经过一次垃圾回收仍然不够,那么将清除软引用的对象)
  • 弱引用:非必要对象,只能生存到下一次垃圾收集为止(弱引用管理的对象,只能存活到下一次垃圾回收)
  • 虚引用:无实在性的作用,唯一目的只是在这个对象被收集时收到一个系统通知(和没有引用是一样的,只是为了系统的检测)

 5.死亡前的最后一根稻草

在以前在刑场上经常会有关键的救命文书,在虚拟机里也有类似的情况,被可达性算法判定为不可达对象,对象只是判为“缓刑”,而不是真正的立即处死,它真正的判断死亡需要2次标记,具体看下图

public class CanReliveObj {

	public static CanReliveObj obj;//类变量,属于 GC Root
	//此方法只能被调用一次
	 @Override
	protected void finalize() throws Throwable {
		//super.finalize();
		System.out.println("调用当前类重写的finalize()方法");
		obj = this;//当前待回收的对象在finalize()方法中与引用链上的一个对象obj建立了联系
	}
	public static void main(String[] args) {
		try {
			obj = new CanReliveObj();
			// 对象第一次成功拯救自己
			obj = null;
			System.gc();//调用垃圾回收器,触发FULL GC  也不是调用后立刻就回收的,因为线程的执行权在操作系统
			System.out.println("第1次 gc");
			// 因为Finalizer线程优先级很低,暂停2秒,以等待它
			Thread.sleep(2000);
			if (obj == null) {
				System.out.println("obj is dead");
			} else {
				System.out.println("obj is still alive");
			}

			System.out.println("第2次 gc");
			// 下面这段代码与上面的完全相同,但是这次自救却失败了
			 obj= null;
			System.gc();
			// 因为Finalizer线程优先级很低,暂停2秒,以等待它
			Thread.sleep(2000);
			if (obj == null) {
				System.out.println("obj is dead");
			} else {
				System.out.println("obj is still alive");
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}


}

6.回收方法区

方法区回收主要是两部分:

1.废弃的常量

2.不在使用的类型

那如何判断一个常量是否废弃呢?下面3个条件至关重要

  • 该类的所有实例均被回收
  • 加载该类的加载器已被回收
  • 该类的java.lang.class对象没有在任何地方进行引用

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃橘子的Crow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值