文章目录

  • 思维导图
  • 概述
  • 如何判断对象为垃圾对象
    • 对象的存活还是死亡
    • 判断对象死亡的算法一:引用计数算法Reference Counting
      • 原理
      • 示意图
      • 优点
      • 缺点
      • 实验
    • 判断对象死亡的算法一:可达性分析算法Reachability Analysis(hotspot采用该算法)
      • 原理
      • 示意图
      • 可作为GC Roots的对象
    • 对象引用的分类
      • 强引用
      • 软引用
      • 弱引用
      • 虚引用
  • finalize 逃逸
    • 原理
    • 实验
    • 注意事项
    • 回收方法区
      • 废弃常量的回收
      • 无用的类的回收

思维导图


概述

谈起垃圾收集 (Garbage Collection ,GC),有3个问题是无法回避的

1. 哪些内存需要回收

2. 什么时候回收

3. 如何回收

这就引出了我们这边博文需要讨论的话题

1. 如何判断对象为垃圾对象

2. 何时回收垃圾对象(垃圾收集算法)

3. 如何回收垃圾对象(垃圾收集器)


我们前面的博文中讨论了Java的内存自动管理机制,我们知道java内存运行时区域可以分为两大部分: 线程共享区域和线程独占区域 。

线程共享区主要包括Java堆(存储对象实例)和方法区(即我们常说的永久代【JDK7之后逐步去永久代,使用元数据区代替】)

线程独占区主要包括:程序计数器、Java虚拟机栈、本地方法栈。 这3个区域以为是线程独占区,因此生命周期同线程相同,随线程而生而灭。 栈中的帧随着方法的进入和退出有条不紊的执行着出栈和入栈操作。 每一个栈帧中分配多少内存基本上在类结构确定下来的时候就已知的,因此线程独享区的内存分配和回收都具备确定性,这几个区域就不需要过多考虑回收的问题,因为方法结束或者线程结束的时候,内存就跟着回收了。

而线程共享区(Java堆和方法区)则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的也是Java堆和方法区。我们这里讨论的内存分配与回收也指的是这一部分.


如何判断对象为垃圾对象

对象的存活还是死亡

Java堆内存中存放着几乎所有的对象实例。

垃圾收集器在对堆内存进行回收之前,需要确定哪些对象是存活或者死去(即不可能再被任何途径使用的对象)


判断对象死亡的算法一:引用计数算法Reference Counting

原理

通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。


示意图


优点

  • 实现简单
  • 判断效率高

缺点

  • 无法解决对象之间相回循环引用的问题,易引起内存泄露

实验

package com.artisan.gc;/*** * * @ClassName: ReferenceCountingGC* * @Description: VM Args* * @author: Mr.Yang* * @date: 2018年7月29日 上午10:31:32*/
public class ReferenceCountingGC {private Object instance;private static final int _1M = 1024 * 1024;// 设置个成员变量,在堆中占点内存,以便观察GC是否回收相互引用的情况private byte[] bigByte = new byte[2 * _1M];public static void main(String[] args) {ReferenceCountingGC rc = new ReferenceCountingGC();ReferenceCountingGC rc2 = new ReferenceCountingGC();// 设置相互引用rc.instance = rc2;rc2.instance = rc;// 将对象置为空rc.instance = null;rc2.instance = null;// 垃圾回收,观察rc 和 rc2能否被回收System.gc();}}

虚拟机参数设置: -verbose:gc -XX:+PrintGCDetails

日志信息:

可以看到确实被回收了,这也侧面验证了我们现在使用的hotspot虚拟机不是采用该算法进行垃圾回收。


判断对象死亡的算法一:可达性分析算法Reachability Analysis(hotspot采用该算法)

原理

通过一系列的称为“GCsRoots”的对象作为起始点,从这些节点向下开始搜索,搜索所走过的路径称为引用链(Reference Chain)。 当一个对象到GC Roots没有任何引用链相连(即从GC Roots到这个对象不可达)时,则证明该对象是不可用的。


示意图

可作为GC Roots的对象

1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
2. 方法区中类静态属性引用的对象
3. 方法区中常量引用的对象
4. 本地方法栈中JNI(即native方法)引用的对象


对象引用的分类

JDK1.2之后,Java对引用的概念进行了扩充。

引用强度 强引用 Strong Reference > 软引用 Soft Reference > 弱引用 Weak Reference > 虚引用 Phantom Reference

一个对象的生命周期:

如果有软引用指向这些对象,则只有在JVM需要内存时才回收这些对象。

如果一个对象只有弱引用指向它,垃圾回收器会立即回收该对象,这是一种急切回收方式。

弱引用和软引用的特殊行为使得它们在某些情况下非常有用。

例如:软引用可以很好的用来实现缓存,当JVM需要内存时,垃圾回收器就会回收这些只有被软引用指向的对象。

而弱引用非常适合存储元数据,例如:存储ClassLoader引用。如果没有类被加载,那么也没有指向ClassLoader的引用。一旦上一次的强引用被去除,只有弱引用的ClassLoader就会被回收


强引用

我们 new 出来的对象 “Object obj = new Object();”或者 String s=”abc”中变量s就是字符串对象”abc”的一个强引用,任何被强引用指向的对象都不能被垃圾回收器回收,这些对象都是在程序中需要的


软引用

如果该对象含有软引用,Counter对象不会立即被回收,除非JVM需要内存。

Java中的软引用使用java.lang.ref.SoftReference类来表示

Counter prime = new Counter(); // prime holds a strong reference
SoftReference soft = new SoftReference(prime) ; //soft reference variable has SoftReference to Counter Object created at line 2prime = null; // now Counter object is eligible for garbage collection but only be collected when JVM absolutely needs memory

强引用置空之后,代码的第二行为对象Counter创建了一个软引用,该引用同样不能阻止垃圾回收器回收对象,但是可以延迟回收,与弱引用中急切回收对象不同。


弱引用

只需要给强引用对象counter赋空值null,该对象就可以被垃圾回收器回收。因为该对象此时不再含有其他强引用,即使指向该对象的弱引用weakCounter也无法阻止垃圾回收器对该对象的回收。

弱引用使用java.lang.ref.WeakReference class 类来表示

Counter counter = new Counter(); // strong reference
WeakReference<Counter> weakCounter = new WeakReference<Counter>(counter); //weak reference
counter = null; // now Counter object is eligible for garbage collection

另一个使用弱引用的例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一种实现。WeakHashMap有一个特点:map中的键值(keys)都被封装成弱引用,也就是说一旦强引用被删除,WeakHashMap内部的弱引用就无法阻止该对象被垃圾回收器回收。


虚引用

虚引用是java.lang.ref package包中第三种可用的引用,使用java.lang.ref.PhantomReference类来表示。拥有虚引用的对象可以在任何时候被垃圾回收器回收。

通过如下代码创建虚引用:

DigitalCounter digit = new DigitalCounter(); // digit reference variable has strong reference – line 3
PhantomReference phantom = new PhantomReference(digit); // phantom reference to object created at line 3digit = null;

一旦移除强引用,第三行的DigitalCounter对象可以在任何时候被垃圾回收器回收。因为只有一个虚引用指向该对象,而虚引用无法阻止垃圾回收器回收对象.


finalize 逃逸

原理

在使用可达性分析算法的虚机中,比如我们常用的hotspot, 当对象不可达时,需要至少经历两次标记过程,才能确定是否要回收。


实验

package com.artisan.gc;public class FinalizeEscapeGC {public static FinalizeEscapeGC SAVE_HOOK = null;public void isAlive() {System.out.println("yes, I am still alive :) -- " + SAVE_HOOK);}// 重写finalize方法,该方法只被调用一次,但并不是调用后立刻被回收@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("finalize method executed!");FinalizeEscapeGC.SAVE_HOOK = this;}public static void main(String[] args) throws InterruptedException {SAVE_HOOK = new FinalizeEscapeGC();/** 拯救成功*/SAVE_HOOK = null;// 提醒虚拟机进行垃圾回收,但是虚拟机具体什么时候进行回收就不知道了System.gc();Thread.sleep(500);if (SAVE_HOOK != null) {SAVE_HOOK.isAlive();} else {System.out.println("No, I am dead :(");}/** 拯救失败*/SAVE_HOOK = null;System.gc();// finalize方法的优先级比较低所以等待它0.5秒Thread.sleep(500);if (SAVE_HOOK != null) {SAVE_HOOK.isAlive();} else {System.out.println("No, I am dead :(");}}
}

输出:

finalize method executed!
yes, I am still alive :) -- com.artisan.gc.FinalizeEscapeGC@5d888759
No, I am dead :(

任何对象的finalize()方法只会被系统自动调用一次。

第一次逃脱成功,原因在于对象重写了finalize()方法,在手动调用System.gc()时触发垃圾回收,在执行finalize()方时, 在其中将 SAVE_HOOK重新用this关键字挂上和当前对象关系,所以在第二次标记时该对象已经不再“待回收”的队列中了,所以此时对象还是存活的;

但是第二次逃亡的时候,不再执行了finalize()方法了(之前执行过一次,对象的finalize()方法必定只执行一次),在SAVE_HOOK至为null后不再可达,finalize()方法也是没有必要执行的情况,所以它就直接为null了,没有指向任何对象,此时对象已死。


注意事项

  • 避免使用finalize(),操作不慎可能导致错误。

  • 优先级低,何时被调用,不确定

  • 何时发生GC不确定,自然也就不知道finalize方法什么时候执行

  • 如果要使用finalize去释放资源,我们可以使用try-catch-finally来替代它


回收方法区

很多人认为方法区(或者Hopspot虚机中的永久代)是没有垃圾收集的,HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区 ,主要回收

  • 废弃常量
  • 无用的类

废弃常量的回收

常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值外,还包含一些以文本形式出现的符号引用,比如:

  • 类和接口的全限定名;
  • 字段的名称和描述符;
  • 方法和名称和描述符。

回收废弃常量和回收Java堆中的对象非常类似。 以常量池中的字面量的回收为例。

假设有一个字符串“abc”已经进入了常量池中,但当前系统中没有任何一个String对象叫做“abc”的,换就话说就是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果这时发生了内不曾能回收,而且有必要的话,这个“abc”就会被系统清理出常量池。 常量池中的其他类(接口)、方字段的符号引用也与此类似。


无用的类的回收

必须同时满足如如下3个条件才能算是“无用的类”

  • 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
  • 加载该类的ClassLoader已经被回收
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

即使同时满足了如上3个条件,hotspot虚机也不一定必然回收,hotspot虚机提供了-Xnoclassgc参数进行控制。 还可以使用-verbose:class 以及-XX:+TraceClassLoading 、-XX:+TraceClassUnLoading查看类加载和卸载信息。

JVM-04垃圾收集Garbage Collection(上)【垃圾对象的判定】相关推荐

  1. 图文理解 JVM GC(Garbage Collection) 垃圾回收

    目录 前言 JVM如何判断一个对象是不是垃圾? 引用计数算法 可达性分析法(主流判断方法)

  2. JVM-05垃圾收集Garbage Collection(中)【垃圾收集算法】

    文章目录 思维导图 标记清除算法(其他算法的基础) 复制算法(新生代的GC) 标记整理/压缩算法(老年代的GC) 标记-清除算法.复制算法.标记整理算法总结 分代收集算法(新生代的GC+老年代的GC) ...

  3. 垃圾收集 (Garbage Collection,GC)

    垃圾收集 (Garbage Collection,GC) 垃圾收集主要是针对堆和方法区进行.程序计数器.虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因 ...

  4. 67.Java垃圾收集机制\对象引用\垃圾对象的判定\垃圾收集算法\标记—清除算法\标记—整理算法\分代收集\垃圾收集器\性能调优

    67.Java垃圾收集机制 67.1.对象引用 67.2.垃圾对象的判定 67.3.垃圾收集算法 67.3.1.标记-清除算法 67.3.2.标记-整理算法 67.3.3.分代收集 67.4.垃圾收集 ...

  5. JVM-07垃圾收集Garbage Collection【GC日志分析】

    文章目录 概述 GC格式 实例 GC监控 安装GC可视化插件 启动本地进程,监控和分析GC情况 GC日志分析工具 概述 每一种收集器的日志形式都是由他们自身的实现决定的,也就是说每个收集器的日志格式都 ...

  6. JVM-08垃圾收集Garbage Collection【GC常用参数】

    文章目录 思维导图 内存相关 策略相关 GC日志相关 异常相关 其他 思维导图 内存相关 策略相关 GC日志相关 异常相关 其他

  7. Java (JVM) Memory Model and Garbage Collection Monitoring Tuning

    java内存模型以及GC(垃圾回收监视器)调优 原文:http://www.journaldev.com/2856/java-jvm-memory-model-and-garbage-collecti ...

  8. FTL——垃圾回收GC (Garbage Collection)

    本文章内容来源<深入浅出SSD 固态存储核心技术 原理与实战.pdf> 简介 由于闪存需要先擦除后才能写入,由于闪存块不能覆盖写,当写人一笔新的数据时,不能直接在老地方更改(闪存不允许在一 ...

  9. JVM 调优系列之图解垃圾回收

    转载自  JVM 调优系列之图解垃圾回收 摘要: jvm必知系列,总结一些常见jvm回收机制,方便查阅 从这篇开始我们开始探讨一些jvm调优的问题.在jvm调优中一个离不开的重点是垃圾回收,当垃圾回收 ...

最新文章

  1. EventBus设计之禅
  2. 解决long类型传到前端损失精度问题
  3. 文献记录(part42)--An end-to-end framework for unconstrained monocular 3D hand pose estimation
  4. 初中数学抽象教学的案例_初中八年级数学上册教学视频汇总
  5. 某工程车零部件制造厂商
  6. 搜索的逻辑即是用户的逻辑
  7. 如何从zabbix数据库中获取每日流量最大值
  8. ReviewBoard代码评审实践总结
  9. SPSS:主成分分析确定不同指标权重
  10. php chinese.php,ChineseUtil
  11. 【Tableau Desktop 企业日常问题29】Tableau desktop 更换电脑的时候 迁移License
  12. DirectX--给视频加马赛克、字符OSD
  13. python中获取中位数的两种方法
  14. 解决多旅行商(MTSP)的分组遗传算法(GGA-SS)
  15. Combination
  16. 漫步在洛杉矶的春天里
  17. PHP框架设计之 ThinkPHP5 源码解析
  18. 功能类微信小程序的推广
  19. 机器学习考点---过拟合与欠拟合、CNN原理......
  20. pgsql实现json格式转换

热门文章

  1. Forth Week :快速上手一门编程语言
  2. php调用接口接口代码无法执行,php调用c接口无错版介绍
  3. matlab编程风格
  4. 论文辅助笔记(代码实现):Bayesian Probabilistic Matrix Factorizationusing Markov Chain Monte Carlo
  5. Flink从入门到精通100篇(二十三)-基于Apache Flink的爱奇艺实时计算平台建设实践
  6. 机器学习:KNN算法(MATLAB实现)
  7. TensorFlow2简单入门-加载及预处理文本
  8. LeetCode题组:第169题-多数元素
  9. 剑指offer:替换空格
  10. elastic-job的原理简介和使用