博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM垃圾回收机制
阅读量:3780 次
发布时间:2019-05-22

本文共 2708 字,大约阅读时间需要 9 分钟。

一、概述

JVM的内存结构包括五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而Java堆区和方法区则不一样,这部分内存的分配和回收是动态的,正是垃圾收集器所需关注的部分。
二、判断对象是否存活
1、引用记数法
引用计数是垃圾收集器中的早期策略。在这种方法中,堆中每个对象实例都有一个引用计数。当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象实例的计数器+1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1。
优缺点
优点:引用计数收集器可以很快的执行,交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。

缺点:无法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0。

public class ReferenceFindTest {    public static void main(String[] args) {        MyObject object1 = new MyObject();        MyObject object2 = new MyObject();                  object1.object = object2;        object2.object = object1;                  object1 = null;        object2 = null;    }}

这段代码是用来验证引用计数算法不能检测出循环引用。最后面两句将object1和object2赋值为null,也就是说object1和object2指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数器都不为0,那么垃圾收集器就永远不会回收它们。

2、可达性分析算法
程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被判定为是可回收的对象。

在这里插入图片描述

在Java语言中,可作为GC Roots的对象包括下面几种:
  a) 虚拟机栈中引用的对象(栈帧中的本地变量表);
  b) 方法区中类静态属性引用的对象;
  c) 方法区中常量引用的对象;
  d) 本地方法栈中JNI(Native方法)引用的对象。
三、Java中的引用
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。在Java语言中,将引用又分为强引用、软引用、弱引用、虚引用4种,这四种引用强度依次逐渐减弱。

强引用

  在程序代码中普遍存在的,类似 Object obj = new Object() 这类引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

软引用

  用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收后还没有足够的内存,才会抛出内存溢出异常。SoftReference类来实现软引用。
弱引用
  也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。WeakReference类来实现弱引用。

虚引用

  也叫幽灵引用或幻影引用(名字真会取,很魔幻的样子),是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。它的作用是能在这个对象被收集器回收时收到一个系统通知。PhantomReference类来实现虚引用。
四、对象的真正死亡
即使在可达性分析算法中不可达的对象,也并非是“非死不可”,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程。

第一次标记:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记;

第二次标记:第一次标记后接着会进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。在finalize()方法中没有重新与引用链建立关联关系的,将被进行第二次标记。

第二次标记成功的对象将真的会被回收,如果对象在finalize()方法中重新与引用链建立了关联关系,那么将会逃离本次回收,继续存活。

五、回收方法区
方法区主要回收的内容有:废弃常量和无用的类。对于废弃常量也可通过引用的可达性来判断,但是对于无用的类则需要同时满足下面3个条件:

该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例;

加载该类的ClassLoader已经被回收;
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾收集算法

1、标记-清除算法
(1)标记
根据之前的判定方法,标记出需要回收的对象,
(2)清除
将垃圾回收。
缺点:
(1)效率问题,效率不高
(2)空间问题,标记清除后会产生大量不连续的内存碎片,空间碎片太多导致程序在分配较大的对象时,无法找到足够的连续内存而不得不触发另一次垃圾收集动作。
在这里插入图片描述
2、复制算法
为了解决效率问题,复制算法将内存划分为相等的两部分,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
在这里插入图片描述
这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。
  很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。
3、标记-整理算法

转载地址:http://ybpvn.baihongyu.com/

你可能感兴趣的文章
MultiSlider组件
查看>>
TransparentBitmap函数设置透明位图的原理分析
查看>>
透明位图的显示(TransparentBlt函数)
查看>>
关于CBitmap,LoadBitmap 的使用
查看>>
VC增强Edit控件为日期输入控件
查看>>
获得实例的类名
查看>>
VC++ Static标签实现背景透明!
查看>>
VC++ Tab控件的背景色的处理
查看>>
谈GDI绘图中的资源释放
查看>>
VC6.0编写层析软件移植到VS2003心得
查看>>
Visual C++头文件互相包含的解决方案
查看>>
工具条研究手记(3)- 工具条源代码剖析
查看>>
变长结构体的几种方法
查看>>
const int *p,const * int p,int const *p
查看>>
批处理延时方法小结
查看>>
dll创建无模式对话框
查看>>
CRectTracker类的使用方法及其静态库下CRectTracker无法显示鼠标光标解决方案
查看>>
VC++深入详解 - 窗口的创建
查看>>
SystemParametersInfo ( SPI_GETNONCLIENTMETRICS... 在VC 2008 里不能正常工作
查看>>
读取其它进程richedit控件的内容
查看>>