深入理解JVM(四) -- 垃圾内存回收的判定方法和内容
上一篇文章我们学到了对象在内存中是如何存储的已经是如何被访问的,这篇文章将介绍当内存空间不够时,虚拟机将怎样判定对象可不可以被回收已经哪些地方会发生回收。
垃圾回收主要(不是全部)发生在堆内存中,当一个对象没有存在的必要的时候,占着内存明显不行,所以Java内置的GC会对没有必要存在的内存区域进行回收,那么,如何判断一个对象已经没有使用价值了,或者说,已死了呢?
首先,最直观的方式是在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就+1,当引用值失效时,再-1,任何时刻计数器值为0的对象就是不可能再被使用的,客观的说,这种方式实现简单,判定效率高,在大部分情况下是一个不错的算法,但是它最大的问题在于不能解决对象之间相互引用的问题,例如:objA.instance = objB; objB.instance = objA; 这样两个对象之间互相引用,引用计数器的值都不为0,但是很明显除此之外没有别的地方需要这两个对象,此时使用这种回收机制就显得无能为力。此时,另一种算法:可达性分析,在主流的商用程序语言中,都使用到了这种算法,这种算法的思路是:从一系列定义为GC-Root的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为“引用链”,当一个对象到GC-Root对象没有任何引用链相连时,即无法到达时,认为该对象已经没用了,这种算法很好的解决了上述中提到的对象之间互相引用的问题。
在Java中,可以作为GC-Roots的对象有以下几种:
虚拟机栈中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈JNI中引用的对象
从上文可知,无论是引用计数器和可达性分析(引用链)都与引用这个概念相关,那么什么是引用呢?在JDK1.2以前,引用的定义很直接:如果reference类型数据中存储的是另一块内存的起始地址,就称这块内存存在着一个引用。这种定义很纯粹,但是有些狭隘,它无法描述那些“食之无味,弃之可惜”的对象,我们希望能描述这样一类对象:当内存空间还足够时,可以保留在内存中,如果内存空间在垃圾回收后还是很紧张,则可以抛弃这些对象,很多系统的缓存功能都符合这样的应用场景。于是,在JDK1.2之后,Java定义了四种引用类型:
1.强引用:在代码中普遍存在,类似"Object a = new Object()"就是强引用,只要强引用存在,垃圾回收器就不会回收被引用的内存。
2.软引用:Soft Reference,用来描述一些有用但不是必须的对象,当将要发生内存溢出时,会将这些对象列为回收范围进行第二次回收,如果这次回收后还没有足够的可用内存,才会抛出内存溢出。
3.弱引用:描述的对象与软引用相似,但是比软引用更弱一些,只能生存到下次GC之前,当虚拟机进行内存回收时,无论是否存在软引用,都会对该对象进行回收,在JDK1.2之后,提供了WeakReference来表示弱引用。
4.虚引用:Phantom Reference,也被称为幽灵引用或幻影引用,是最弱的一种引用关系,他的存在不会对对象的生存时间构成任何影响,也无法通通过虚引用获取一个对象实例,引入它只是为了在被系统回收时得到一个通知。
tips:当一个对象被可达性算法和引用计数法都判定为“死亡”时,还会进行一次筛选,如果这个类覆盖了Object类的finalize()方法(对于一个对象,这个方法只会调用一次),且可以调用时,会先调用该方法,执行完该方法后如果还是没有满足可存活的条件,才会将其回收,这是文章作者特意提到的,对象可以在该方法中进行“自救”,例如:

将自己this赋值给一个引用,但是只能自救一次,因为该方法只会被调用一次。
但是作者建议不要使用这种方式拯救对象,因为调用不确定性大,开销大,用finalize()可以做到的,用try-finally可以做的更好,只须了解有这个方法存在即可。
方法区也是会发生内存回收的
很多人会认为方法区中不会进行内存回收的操作,虽然在方法区进行内存回收的效率比较低(正常在堆中进行一次GC大约可以释放70%~95%的内存空间),但是还是会执行内存回收,回收的对系那个有两种,一是废弃的常量,二是无用的类。
1.例如一个常量“abc”已经进入了常量池,但是当前系统没有一个String对象是指向该常量,如果这时候发生了内存回收,并且有必要的话,就会将该常量回收。
2.判断一个常量是否无用简单,但是判断一个类是否无用比较复杂,需要满足多个条件,首先,该类的所有实例均以被回收,也就是Java堆中不存在该类的实例;其次,该类的ClassLoader已经被回收;并且,该类的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。虚拟机可以对上述类进行回收,但是仅仅是可以,而不是像对象一样,不使用了就必然会回收。
下一章会介绍虚拟机主要使用的几种垃圾回收算法。
深入理解JVM(四) -- 垃圾内存回收的判定方法和内容的更多相关文章
- 图解JVM垃圾内存回收算法
图解JVM垃圾内存回收算法 这篇文章主要介绍了图解JVM垃圾内存回收算法,由于年轻代堆空间的垃圾回收会很频繁,因此其垃圾回收算法会更加重视回收效率,下面博主和大家来一起学习一下吧 前言 首先,我们要讲 ...
- 推荐收藏系列:一文理解JVM虚拟机(内存、垃圾回收、性能优化)解决面试中遇到问题(图解版)
欢迎一起学习 <提升能力,涨薪可待篇> <面试知识,工作可待篇 > <实战演练,拒绝996篇 > 欢迎关注我博客 也欢迎关注公 众 号[Ccww笔记],原创技术文章 ...
- JVM(四)内存回收(二)
在上一节中"JVM(三)内存回收(一)"我讲到了垃圾回收的几种算法,算是解决了之前提到的3个问题中的最后一个. 关于内存回收,还应该了解常用的内存回收器(GC Collector) ...
- 深入理解JVM一垃圾回收算法
我们都知道java语言与C语言最大的区别就是内存自动回收,那么JVM是怎么控制内存回收的,这篇文章将介绍JVM垃圾回收的几种算法,从而了解内存回收的基本原理. 一.stop the world 在介绍 ...
- JVM(四) 垃圾回收
1. 堆内存结构 Java堆从GC的角度可以细分为:新生代(Eden区.From Survivor区和To Survivor区)和老年代. 1.1 新生代 新生代是用来存放新生的对象.一般占据堆的1/ ...
- 深入理解JVM——关于垃圾回收
关于垃圾回收 仿佛来自上海居委会大妈的灵魂拷问:“你是什么垃圾?” 不 今天我们要说的是JVM的垃圾回收 假如我是一个“人”类的“对象”,也和人的生命一样必有一死,可是“我真的还想再活500年~~”, ...
- 深入理解JVM(五) -- 垃圾回收算法
上篇文章我们了解到哪些内存区域和哪些对象可以被回收,这篇文章我们就来了解一下具体的垃圾回收算法的思路,不讨论具体的实现. 一 最基础算法 标记-清除(Mark-Swap) 为什么说他是最基础的算法,因 ...
- 理解JVM之Java内存区域
Java虚拟机运行时数据区分为以下几个部分: 方法区.虚拟机栈.本地方法栈.堆.程序计数器.如下图所示: 一.程序计数器 程序计数器可看作当前线程所执行的字节码行号指示器,字节码解释器工作时就是通过改 ...
- 深入理解JVM - 1 - Java内存区域划分
作者:梦工厂链接:https://www.jianshu.com/p/7ebbe102c1ae来源:简书简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处. Java与C++之间有一堵 ...
随机推荐
- java中&& 的运算优先级高于||
public class First { public static void main(String[] args) { boolean a = false; boolean b = true; b ...
- 2019暑假Java学习笔记(二)
目录 基础语法(下) 流程控制 if语句 switch语句 while语句和do-while语句 for语句 break关键字 continue关键字 数组 一维数组 二维数组 用户输入操作 练习题: ...
- Linux在线安装Redis
一.进入Redis官网寻找需要下载的版本:https://redis.io/ 将下载地址链接复制下来:http://download.redis.io/releases/redis-5.0.7.tar ...
- Thinking In SE
各种编程范式的区别 并发模型 并行架构: 位级(bit-level)并行 指令级(instruction-level)并行 数据级(data)并行 数据级并行 任务级(task-level)并行 -- ...
- Cesium的Property机制总结[转]
https://www.jianshu.com/p/f0b47997224c 前言 Cesium官方教程中有一篇叫<空间数据可视化>(Visualizing Spatial Data).该 ...
- C# TcpListener TcpClient
C# TcpListener TcpClient 使用,新建从控制台项目,引用System.Net 代码如下: using System; using System.Collections.Gener ...
- JAVA微信开发-如何保存包含特殊字符的微信昵称
我们在做微信开发的时候,有一个很重要的就是通过openid获取用户的详细信息,包含昵称,头像,省,市,区的信息,但是现在移动时代,很多人追求个性,在名字当中大量使用火星文或者表情符.(本人实际测试过一 ...
- redis连接时报错:Could not connect to Redis at 127.0.0.1:6379: Connection refused
在/etc目录下找到redis.conf 将daemonize no 修改为 daemonize yes 再输入 redis-server /usr/local/etc/redis.conf redi ...
- preg_match 第三个参数,
//请修改变量p的正则表达式,使他能够匹配str中的姓名 $p = '/name:([\w\s]+)/'; $str = "name:steven jobs"; preg_matc ...
- [LeetCode] 712. Minimum ASCII Delete Sum for Two Strings 两个字符串的最小ASCII删除和
Given two strings s1, s2, find the lowest ASCII sum of deleted characters to make two strings equal. ...