一.需要回收的内存区域    

程序计数器、虚拟机栈、本地方法栈 3 个区域随线程生灭(因为是线程私有),栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。而 Java 堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期才知道那些对象会创建,这部分内存的分配和回收都是动态的,垃圾回收期所关注的就是这部分内存。
二.垃圾收集算法

主要完成两个功能:检测出垃圾对象;回收垃圾对象占用的空间并归还给程序。

1.如何检测出垃圾对象?

(1)引用计数算法

原理:堆中的每一个对象有一个引用计数,当一个对象被创建,并把指向该对象的引用赋值给一个变量时,引用计数置为1,当再把这个引用赋值给其他变量时,引用计数加1,当一个对象的引用超过了生命周期或者被设置为新值时,对象的引用计数减1,任何引用计数为0的对象都可以被当成垃圾回收。当一个对象被回收时,它所引用的任何对象计数减1,这样,可能会导致其他对象也被当垃圾回收。

问题

  • jdk从1.2开始增加了多种引用方式:软引用、弱引用、虚引用,且在不同引用情况下程序应进行不同的操作。如果我们只采用一个引用计数法来计数无法准确的区分这么多种引用的情况。
  • 如果一个对象A持有对象B,而对象B也持有一个对象A,那发生了类似操作系统中死锁的循环持有,这种情况下A与B的counter恒大于1,会使得GC永远无法回收这两个对象。

在代码objA = null 和 objB = null 之前,内存结构示意图如下:

在代码objA = null 和 objB = null 之后,内存结构示意图如下:

(2)可达性分析算法

原理:程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

一般来说,如下情况的对象可以作为GC Roots:

  • 虚拟机栈(栈桢中的本地变量表)中的引用的对象
  • 方法区中的类静态属性引用的对象
  • 方法区中的常量引用的对象
  • 本地方法栈中JNI(Native方法)的引用的对象

下面给出一个GCRoots的例子,如下图,为GCRoots的引用链。

由图可知,obj8、obj9、obj10都没有到GCRoots对象的引用链,即便obj9和obj10之间有引用链,他们还是会被当成垃圾处理,可以进行回收。

2.引用

无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有关。JDK1.2以后,Java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)

(1)强引用

以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

(2)软引用(SoftReference)

如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。

(3)弱引用(WeakReference)

如果一个对象只具有弱引用,那就类似于可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

(4)虚引用(PhantomReference)

"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃 圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是 否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生

3.不可达的对象并非“非死不可”

即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。

4.如何判断一个常量是废弃常量

运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?

假如在常量池中存在字符串 "abc",如果当前没有任何String对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量,如果这时发生内存回收的话而且有必要的话,"abc" 就会被系统清理出常量池。

5.如何判断一个类是无用的类

方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?

判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是 “无用的类”

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

虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。

6.垃圾收集算法

  • 标记清除算法
  • 复制算法
  • 标记整理算法
  • 分收代集算法

(1)标记清除算法

首先标记出所有需要回收的对象,使用可达性分析算法判断一个对象是否为可回收,在标记完成后统一回收所有被标记的对象。下图是算法具体的一次执行过程后的结果对比。

说明:1.效率问题,标记和清除两个阶段的效率都不高。2.空间问题,标记清除后会产生大量不连续的内存碎片,以后需要给大对象分配内存时,会提前触发一次垃圾回收动作。

(2)复制算法

将内存分为两等块,每次使用其中一块。当这一块内存用完后,就将还存活的对象复制到另外一个块上面,然后再把已经使用过的内存空间一次清理掉。图是算法具体的一次执行过程后的结果对比。

说明:1.无内存碎片问题。2.可用内存缩小为原来的一半。 3.当存活的对象数量很多时,复制的效率很慢。

(3)标记整理算法

标记过程还是和标记 - 清除算法一样,之后让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存,标记 - 整理算法示意图如下:

说明:1.无需考虑内存碎片问题。

(4)分代收集算法

把堆分为新生代和老年代,然后根据各年代的特点选择最合适的回收算法。在新生代基本上都是朝生暮死的,生存时间很短暂,因此可以采拥标记 - 复制算法,只需要复制少量的对象就可以完成收集。而老年代中的对象存活率高,也没有额外的空间进行分配担保,因此必须使用标记 - 整理或者标记 - 清除算法进行回收。

java虚拟机之垃圾回收机制的更多相关文章

  1. 【java虚拟机】垃圾回收机制详解

    作者:平凡希 原文地址:https://www.cnblogs.com/xiaoxi/p/6486852.html 一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分 ...

  2. Java 虚拟机 - GC 垃圾回收机制分析

    Java 垃圾回收(Garbage Collection,GC) Java支持内存动态分配.垃圾自动回收,而 C++ 不支持.我想这可能也是 为什么 Java 脱胎于 C++ 的一个原因吧. GC 的 ...

  3. 深入理解java虚拟机---3垃圾回收机制GC

    本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...

  4. java虚拟机 之 垃圾回收机制

    一.如何判断对象已死 垃圾回收器并不是java独有的,垃圾回收器的作用就是回收对象释放内存空间,那么如何判断哪些对象应该被回收呢? 在Java语言中是采用GC Roots来解决这个问题.如果一个对象和 ...

  5. java虚拟机(五)--垃圾回收机制GC5

    什么样的对象需要回收 如果对象已经死亡了,就可以进行回收,判断方式如下 1).引用计数器:给对象添加一个计数器,有地方引用,就+1,当引用失效,就-1.当计数器为0时,判断对象不能再使用,但是当对象相 ...

  6. Java虚拟机之垃圾回收详解一

    Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这 ...

  7. Java虚拟机之垃圾回收

    简述 Java与那些较传统的语言比如C++有个很大不同就是垃圾回收策略了.前者通常是虚拟机自动帮我们做了,而后者就需要我们手动来完成. Java虚拟机帮我们完成了垃圾回收,是不是意味着我们就不用完全去 ...

  8. java中存在垃圾回收机制,但是还会有内存泄漏的问题,原因是

    答案是肯定的,但不能拿这一句回答面试官的问题.分析:JAVA是支持垃圾回收机制的,在这样的一个背景下,内存泄露又被称为“无意识的对象保持”.如果一个对象引用被无意识地保留下来,那么垃圾回收器不仅不会处 ...

  9. 每日一问:讲讲 Java 虚拟机的垃圾回收

    昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...

随机推荐

  1. LeetCode练题——58. Length of Last Word

    1.题目 58. Length of Last Word——Easy Given a string s consists of upper/lower-case alphabets and empty ...

  2. 排序算法之选择排序的python实现

    选择排序算法的工作原理如下: 1. 首先在序列中找到最小或最大元素,存放到排序序列的前或后. 2. 然后,再从剩余元素中继续寻找最小或最大元素. 3. 然后放到已排序序列的末尾. 4. 以此类推,直到 ...

  3. laravel 监听模型创建事件

    注意:

  4. C:sizeof 运算符

    sizeof不是函数,所以不需要包含任何头文件,它的功能是计算一个数据类型的大小,单位为字节 sizeof的返回值为size_t size_t类型在32位操作系统下是unsigned int,是一个无 ...

  5. 开源沙箱CuckooSandbox 介绍与部署

    1. 介绍 1.1应用  在工作中很多时候需要自己对一些可以程序,可执行文件进行检测,当然我们可以通过VT,微步,等一些开源的平台进行检测.现在我们通过自己搭建的开源的沙箱进行检测.所谓沙箱,是分离运 ...

  6. EXPOSE ocker run -P 时,会自动随机映射 EXPOSE 的端口

    EXPOSE 声明端口 格式为 EXPOSE <端口1> [<端口2>...]. EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用 ...

  7. express 应用创建及app.js详解

    #1 express 应用创建 1.安装node.js (自行百度) 2.npm install express -g 3.全局安装express生成器 express-generator npm i ...

  8. Codeforces Round #619 (Div. 2) B. Motarack's Birthday

    Dark is going to attend Motarack's birthday. Dark decided that the gift he is going to give to Motar ...

  9. Nginx 七层反向代理

    目录 1.代理 2.正向代理 3.反向代理 4.Nginx 反向代理 5.Nginx 反向代理相关指令介绍 ①.listen ②.server_name ③.location ④.proxy_pass ...

  10. [经验] 如何将 Java 项目发布到云服务器上并可以访问

    环境: 云服务器 Linux centos7.6 1: 安装 Tomcat  (apache-tomcat-9.0.26.tar.gz) 下载压缩包 --> 通过SSH上传到云服务器 --> ...