JAVA虚拟机06-垃圾回收及引用类型
Java和C++之间有一堵由内存自动分配和垃圾收集技术围成的高墙
1.了解垃圾收集、内存自动分配的意义
2.JAVA虚拟机各个区域的垃圾回收简介
3.判断对象是否存活
3.1引用计数算法
3.2可达性分析算法
3.2.1简介
3.2.2GC Roots说明
3.3引用简介
3.4真正的死亡回收
4.方法区垃圾回收
4.1废弃的常量
4.2无用的类型
1.了解垃圾收集、内存自动分配的意义
当需要排查各种内存溢出、内存泄漏问题时,当垃圾回收成为高并发的瓶颈时,就需要我们对这些自动化的技术进行监控和调节
2.JAVA虚拟机各个区域的垃圾回收简介
对于程序计数器、虚拟机栈、本地方法栈,他们的生命周期和线程同步,栈中的栈帧随着方法的执行进栈和出栈。它们的回收相对简单,随着线程或者方法的结束就可以回收。而JAVA堆和方法区(永久代/元空间)的回收相对复杂。
3.判断对象是否存活
3.1引用计数算法
当一个对象被引用时,计数器值加1,当引用失效时,计数值减1,当对象没有被引用时,认为对象已死。在主流的JAVA虚拟机里面都没有使用这个算法。因为这个算法看起来很简单,但是却需要考虑很多特殊的情况,需要配合大量的额外处理才能够确保工作的额正确进行。比如两个对象之间的相互循环引用。
3.2可达性分析算法
3.2.1简介
主流的商用程序语言,都是采用的这个算法
这个算法的基本思路就是通过一系列被称为"GC Roots"的根对象作为起始节点集,从这些节点开始,通过运用关系向下搜索,搜索过程的路径成为"引用链"。如果某个对象到"GC Roots"之间没有引用链,则证明该对象不可能再被使用。
如下图,5/6/7之间有引用,但是没有连接到GC Roots,所以判定它们是可回收对象

3.2.2 GC Roots说明
在Java语言中,可作为GC Roots的对象包含以下几种:
1)虚拟机栈(栈帧中的本地变量表)中引用的对象。
2)方法区中静态属性引用的对象,比如JAVA类的引用类型静态变量
3)方法区中常量引用的对象
4)本地方法栈中(Native方法)引用的对象
5)所有被同步锁(synchronized)持有的对象
6)反映了虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
7)除了上面这些固定的GC Roots集合外,根据用户选用的垃圾回收器以及当前回收的内存区域不同,还可以有其它对象临时性的加入。
(1)首先第一种是虚拟机栈中的引用的对象,我们在程序中正常创建一个对象,对象会在堆上开辟一块空间,同时会将这块空间的地址作为引用保存到虚拟机栈中,如果对象生命周期结束了,那么引用就会从虚拟机栈中出栈,因此如果在虚拟机栈中有引用,就说明这个对象还是有用的,它就会作为GC Root。
(2)第二种是我们在类中定义了全局的静态的对象,也就是使用了static关键字,由于虚拟机栈是线程私有的,所以这种对象的引用会保存在共有的方法区中,显然将方法区中的静态引用作为GC Roots是必须的。
(3)第三种便是常量引用,就是使用了static final关键字,由于这种引用初始化之后不会修改,所以方法区常量池里的引用的对象也应该作为GC Roots。
(4)最后一种是在使用JNI技术时,有时候单纯的Java代码并不能满足我们的需求,我们可能需要在Java中调用C或C++的代码,因此会使用native方法,JVM内存中专门有一块本地方法栈,用来保存这些对象的引用,所以本地方法栈中引用的对象也会被作为GC Roots。
3.3 引用简介
在JDK1.2以前,Java中引用的定义很传统: 如果引用类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。这种定义有些狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态。
我们希望能描述这一类对象: 当内存空间还足够时,则能保存在内存中;如果内存空间在进行垃圾回收后还是非常紧张,则可以抛弃这些对象。很多系统中的缓存对象都符合这样的场景。
在JDK1.2之后,Java对引用的概念做了扩充,将引用分为强引用(Strong Reference)、软引用(Soft
Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种,这四种引用的强度依次递减。
1)强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 ps:强引用其实也就是我们平时A
a = new A()这个意思。
2)软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,会把这些对象列入回收范围进行第二次回收。软引用可用来实现内存敏感的高速缓存。一般可以用来实现缓存。
3)弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。ThreadLocal就用到它了。
4)虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动,管理对外内存。因为虚引用对象被回收的时候,会有一个通知。
3.4真正的死亡回收
即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。
1)判断对象是否在GC Roots引用链上,如果不在,进行第一次标记
2)判断该对象是否有必要执行的finalize()方法。当对象没有覆盖(重写)finalize方法,或者finzlize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,对象被回收
3) 如果这个对象被判定为有必要执行finalize()方法,执行finalize()方法,再判断是否在GC Roots引用链上,不在,对象被回收
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。Finalize()方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己----只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。
示例:下面示例演示垃圾回收时finalize方法被系统自动调用
package com.jenkin.wx.Test; /**
* @Classname GcClass2
* @Description TODO
* @Date 2021/11/1 0001 下午 3:33
* @Created by jcc
*/
public class GcClass2 { public static GcClass2 c = null; @Override
protected void finalize() throws Throwable { //重写finalize()方法
super.finalize();
System.out.println("finalize方法执行");
c = this;
} public static void main(String[] args) throws InterruptedException {
c = new GcClass2(); //创建个对象
c = null; //对象不再被引用
System.gc(); //垃圾回收
Thread.sleep(200); //Thread.sleep(200),等待垃圾回收、finalize()方法执行
if(c == null){
System.out.println("被回收了");
}else{
System.out.println("活着");
}
//这里的代码上面的代码一模一样,同样的代码执行两遍
c = null;
System.gc();
Thread.sleep(200);
if(c == null){
System.out.println("被回收了");
}else{
System.out.println("活着");
}
}
}
执行
finalize方法执行
活着
被回收了
发现,第一个判断中,c不为null,因为垃圾回收时,执行了finalize方法,这个对象被赋值给变量c了
第二个片段中,c为null,说明垃圾回收时,finalize方法没有再次执行。
因为任何一个对象的finalize方法都只会被系统自动调用一次。
注意:finalize方法被官方申明为不推荐使用的语法,不推荐使用。
4.方法区垃圾回收
很多人以为方法区(或者HotSopt VM中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且性价比一般较低,在对的新生代生一般能回收70%~95%的空间,而永久代远低于此。
方法区垃圾回收主要包含两块内容。废弃的常量和不在使用的类型。
4.1废弃的常量
回收废弃常量与回收Java堆中的对象非常相似。以常量池中字面量的回收为例,若字符串“abc”已经进入常量池中,但当前系统没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用该字面量,若发生内存回收,且必要的话,该“abc”就会被系统清理出常量池。常量池中其他的类(接口)、方法、字段的符号引用与此类似。
4.2无用的类型
需要满足3个条件:
(1)该类所有的实例都已经被回收,即Java堆中不存在该类的任何实例;
(2)加载该类的ClassLoader已经被回收;
(3)该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3个条件的无用类进行回收,此处仅仅是“可以”,而不是必然回收
JAVA虚拟机06-垃圾回收及引用类型的更多相关文章
- Java虚拟机之垃圾回收详解一
Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这 ...
- 每日一问:讲讲 Java 虚拟机的垃圾回收
昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...
- Java虚拟机之垃圾回收
简述 Java与那些较传统的语言比如C++有个很大不同就是垃圾回收策略了.前者通常是虚拟机自动帮我们做了,而后者就需要我们手动来完成. Java虚拟机帮我们完成了垃圾回收,是不是意味着我们就不用完全去 ...
- Java 虚拟机 - GC 垃圾回收机制分析
Java 垃圾回收(Garbage Collection,GC) Java支持内存动态分配.垃圾自动回收,而 C++ 不支持.我想这可能也是 为什么 Java 脱胎于 C++ 的一个原因吧. GC 的 ...
- java虚拟机 之 垃圾回收机制
一.如何判断对象已死 垃圾回收器并不是java独有的,垃圾回收器的作用就是回收对象释放内存空间,那么如何判断哪些对象应该被回收呢? 在Java语言中是采用GC Roots来解决这个问题.如果一个对象和 ...
- Java 虚拟机的垃圾回收
背景 垃圾收集(Garbage Collection,GC),GC的历史比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言. 对于Java来说,运行时区域 ...
- 深入理解java虚拟机---3垃圾回收机制GC
本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...
- 【java虚拟机】垃圾回收机制详解
作者:平凡希 原文地址:https://www.cnblogs.com/xiaoxi/p/6486852.html 一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分 ...
- java虚拟机之垃圾回收算法
标记-清除算法: 这是最基础的,就是之前所讲的两次标记,首先标记出所有 需要回收的对象,然后进行统一清除, 这有两缺点:一是效率低,标记和清除(开启低优先级进行回收)都是低效率的.第二是空间问题,标记 ...
- 深入理解java虚拟机【垃圾回收算法】
Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...
随机推荐
- CSP-J2022 题解报告
\(CSP-J2022\) 题解报告 \(T1\) 乘方: 发现 \(2^{32}>10^9\),所以这个题只需要特判 \(a=1\) 的情况为 \(1\),其他直接枚举再判断即可. Code: ...
- 你认识的C# foreach语法糖,真的是全部吗?
本文的知识点其实由golang知名的for循环陷阱发散而来, 对应到我的主力语言C#, 其实牵涉到闭包.foreach.为了便于理解,我重新组织了语言,以倒叙结构行文. 先给大家提炼出一个C#题:观察 ...
- 【每日一题】【迭代器,泛型】2022年1月8日-NC93 设计LRU缓存结构
描述设计LRU(最近最少使用)缓存结构,该结构在构造时确定大小,假设大小为 k ,并有如下两个功能1. set(key, value):将记录(key, value)插入该结构2. get(key): ...
- 01-复杂度2 Maximum Subsequence Sum (25分)
Sample Input: 10 -10 1 2 3 4 -5 -23 3 7 -21 Sample Output: 10 1 4 题目有一个测试点是"最大和前面有一段是0",所以 ...
- (java 实现开箱即用基于 redis 的分布式锁
项目简介 lock 为 java 设计的分布式锁,开箱即用,纵享丝滑. 开源地址:https://github.com/houbb/lock 目的 开箱即用,支持注解式和过程式调用 基于 redis ...
- 什么是JS?JS的用途?
一.JavaScript是什么?它有什么作用? JavaScript是一种运行在客户端的脚本语言,简称JS,属于解释性语言.它是一行翻译执行完以后再进行下一行,代码不进行预编译. JavaScript ...
- 前端内容(HTML CSS javaScript)
前端内容 前端基础之HTML 前端基础之HTML HTML标签使用 HTML之form表单 前端基础之CSS 前端基础之CSS CSS字体颜色 背景 盒子模型 CSS浮动 溢出 头像框 CSS定位 i ...
- 为什么总是应该考虑给定 List 的初始大小
在 .Net 技术中,使用 List<> 来存储数据是很常见的.List<> 是一个可以动态增长的泛型集合类型,可以存储任何类型的数据. 但是,在实际使用中,很多人并不注意给定 ...
- pytest常用参数汇总
1. -s 表示输出调试信息,包括print打印信息 D:\demo>pytest -s ./pytest_1 ===================================== ...
- css images图片铺满 不变型 以及头像裁剪 属性
一,图片的引入 background:url(img_flwr.gif); background-repeat:no-repeat; //平铺 二,图片的大小不不变形 background-size: ...