程序计数器、虚拟机栈、本地方法栈随线程而生,随线程而灭。

栈帧随着方法的开始而入栈,随着方法的结束而出栈。

这几个区域的内存分配和回收都具有确定性,在这几个区域内不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。

对于 Java 堆和方法区,只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的正是这部分内存。

一、判定对象是否存活

若一个对象不被任何对象或变量引用,那么它就是无效对象,需要被回收。

可达性分析法(图论)

所有和 GC Roots 直接或间接关联的对象都是有效对象,和 GC Roots 没有关联的对象就是无效对象。

GC Roots 指:
Java 虚拟机栈(栈帧中的本地变量表)中引用的对象
本地方法栈中引用的对象
方法区中常量引用的对象
方法区中类静态属性引用的对象

GC Roots 并不包括堆中对象所引用的对象,这样就不会有循环引用的问题。

二、引用的种类

不同的引用类型,主要体现的是对象不同的可达性状态和垃圾收集的影响。

// 强引用,永远不会回收被引用的对象,会产生内存泄漏
String str = "abc"; // 软引用,内存不足时被回收
SoftReference<String> sr = new SoftReference<>("abc"); // 弱引用,无论内存是否充足,都会被回收
WeakReference<String> wr = new WeakReference<>("abc"); // 虚引用,和没有引用一样,用来感知对象被 GC 的时机(存入队列中),或用来堆外内存释放
ReferenceQueue rq = new ReferenceQueue<>();
PhantomReference<String> pr = new PhantomReference<>("abc", rq); // 建议 JVM 进行 Full GC,而非一定
// 只让虚拟机自己去管理内存,可以通过 -XX:+ DisableExplicitGC 来禁止调用 System.gc()
System.gc();

三、回收内存

堆区

对于可达性分析中不可达的对象,也并不是没有存活的可能。如果在执行 finalize() 方法时,将 this 赋给了某一个引用,那么该对象就重生了。但 finalize() 方法只会被系统自动调用一次,下一次回收,它的 finalize() 方法不会被再次执行。

JVM 会判断此对象是否有必要执行 finalize() 方法,如果对象没有覆盖 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过,那么对象基本上就真的被回收了。

如果对象被判定为有必要执行 finalize() 方法,那么对象会被放入一个 F-Queue 队列中,虚拟机会以较低的优先级执行这些 finalize()方法,但不会确保所有的 finalize() 方法都会执行结束。如果 finalize() 方法出现耗时操作,虚拟机就直接停止指向该方法,将对象清除。

方法区

方法区中存放生命周期较长的类信息、常量、静态变量,每次垃圾收集只有少量的垃圾被清除。方法区中主要清除两种垃圾:

  • 废弃常量(常量池中的常量不被任何变量或对象引用,那么这些常量就会被清除掉)
  • 无用的类

判定一个类是否是“无用的类”,条件较为苛刻:

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

一个类被虚拟机加载进方法区,那么在堆中就会有一个代表该类的对象:java.lang.Class。这个对象在类被加载进方法区时创建,在方法区该类被删除时清除。

四、常见垃圾收集算法

标记-清除算法(Mark-Sweep,操作清理对象)

判断哪些数据需要清除,并对它们进行标记,然后清除被标记的数据。

Mark-Sweep 之后会产生大量不连续的内存碎片,碎片太多可能导致以后需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法(操作存活对象)

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完,需要进行垃圾收集时,就将存活者的对象复制到另一块上面,然后将第一块内存全部清除。

不会有内存碎片的问题,但内存缩小为原来的一半,浪费空间。适用于存活率低的情况。

标记-整理算法(操作清理对象)

将废弃对象做上标记,然后将未标记的对象移到一边,最后清空另一边区域即可。

这样就不用浪费内存空间,也避免的碎片化问题。适用于存活率高的情况。

分代收集算法(HotSpot 虚拟机 GC 采用分代收集算法)

根据对象存活周期的不同,将内存划分为几块。一般是把 Java 堆分为新生代和老年代,根据年代的特点来选择最佳的收集算法。

  • 新生代:复制算法
  • 老年代:标记-整理算法

堆大小=新生代+老年代(默认分别占堆空间为1/3、2/3),新生代又被分为Eden、from survivor、to survivor(默认8:1:1)

这样划分是为了更好的管理堆内存中的对象,方便 GC 算法来进行垃圾回收。

对象的分配通常在 Eden 中(大对象(需要大量连续内存空间的 Java 对象,如很长的字符串或数据)直接进入老年代,-XX:PretenureSizeThreshold)。

当 Eden 区满后,会触发 Minor GC,把 Eden 区和 from survivor 区中存活的对象进行转移,其中到达年龄(经过多次Minor GC)的会被放入老年代,未到达年龄的放入 to survivor 区。

然后清空 Eden 区和 from survivor 区,交换 from survivor 与 to survivor 的名字。

若存活对象大于 to survivor 区容量,则会被直接放入老年代。若打开了自适应(-XX:+AdaptiveSizePolicy),GC会自动重新调整新生代大小。

若老年代满了,则触发 Full GC。

Minor GC vs Major GC/Full GC:

  • Minor GC:回收新生代(包括 Eden 和 Survivor 区域),因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。
  • Major GC / Full GC: 回收老年代,出现了 Major GC,经常会伴随至少一次的 Minor GC,但这并非绝对。Major GC 的速度一般会比 Minor GC 慢 10 倍 以上。

在 JVM 规范中,Major GC 和 Full GC 都没有一个正式的定义,所以有人也简单地认为 Major GC 清理老年代,而 Full GC 清理整个内存堆。


https://github.com/doocs/jvm/blob/master/docs/03-gc-algorithms.md

https://github.com/doocs/jvm/blob/master/docs/05-memory-allocation-gc.md

https://www.javacodegeeks.com/2015/03/minor-gc-vs-major-gc-vs-full-gc.html

https://www.journaldev.com/2856/java-jvm-memory-model-memory-management-in-java

Java-GC 垃圾收集算法的更多相关文章

  1. JAVA GC垃圾收集器的分析

    本篇文章主要介绍了"JAVA GC垃圾收集器的分析",主要涉及到JAVA GC垃圾收集器的分析方面的内容,对于JAVA GC垃圾收集器的分析感兴趣的同学可以参考一下.       ...

  2. 深入理解Java虚拟机 - 垃圾收集算法与垃圾收集器

    1. 垃圾收集算法       JVM的垃圾收集算法在不同的JVM实现中有所不同,且在平时工作中一般不会深入到收集算法,因此只对算法做较为简单的介绍.       1.1 标记-清除算法        ...

  3. (转)Java GC基本算法

    http://blog.csdn.net/heyutao007/article/details/38151581 1.引用计数(reference counting)    原理:此对象有一个引用,则 ...

  4. java - GC垃圾收集器详解(一)

    概要 该图标记了在jdk体系中所使用到的垃圾收集器及对应的关系图.图片上方为年轻代的垃圾收集器而图片下方是老年代的垃圾收集器.当选择某一个区域的垃圾收集器时会自动选择另外一个区域的另一个垃圾收集器.例 ...

  5. 深入理解Java虚拟机-垃圾收集算法

    一.判断对象是否可进行回收 1.引用计数算法 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就是不可能再被使用的.但是主流的 ...

  6. GC垃圾收集算法

    JVM中的垃圾收集算法实现涉及大量的程序细节,而且各个平台的虚拟机操作内存的方法又各不相同,这里介绍几种垃圾收集算法的思想. 1.标记-清除算法 这是最基础的垃圾收集算法,分为“标记”和“清除”两个阶 ...

  7. Java虚拟机垃圾收集算法

    1.标记-清除算法 标记-清除算法分为 "标记" 和 "清除" 两个步骤:首先标记出所有需要回收的对象,然后在标记完成后统一回收所有被标记的对象,是垃圾收集算法 ...

  8. java - GC垃圾收集器详解(三)

    以前收集器的特点 年轻代和老年代是各自独立且连续的内存块 年轻代收集必须使用单个eden+S0+S1进行复制算法 老年代收集扫描整个老年代区域 都是以尽可能少而快速地执行GC为设计原则 G1是什么 G ...

  9. Java 虚拟机-垃圾收集算法

    本文主要介绍Java虚拟机的垃圾回收算法. 一.概述 二.标记-清除算法 Mark-Sweep.如同名字,该算法分两步: 标记:标记处需要回收的对象 清除:标记完成后统一回收被标记的对象. 缺点: 效 ...

  10. java - GC垃圾收集器详解(二)

    CMS收集器 CMS收集器(ConcurrentMarkSweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器. 适合应用在互联网站或者B/S系统的服务器上,这类应用尤其重视服务器的响应 ...

随机推荐

  1. nginx php-fpm环境搭建权限问题

    如果nginx的work process和php-fpm的运行权限相同,在logrotate的影响下,会导致被上传webshell后 被修改accesslog 故安全配置: nginx.conf: u ...

  2. 4.移动端自动化测试-API讲解

    一.前置代码 1.导入driver对象    from appium import webdriver 2.声明手机驱动对象  只有声明驱动对象我们才可以让手机完成脚本的操作    driver = ...

  3. Cannot assign to read only property 'exports' of object at webpack ....BaseClient

    网上找了很多资料说是import和export不能一起用,改代码 其实根本原因是es6和es5混合使用造成的兼容性问题 只需要配置.babelrc就可以了 首先安装 npm install -D tr ...

  4. webconfig中的&符号问题解决

    第一种解决方案 解决方法是将“&”,用“*”代替,取的时候再替换 第二种解决方案 用“&”替换“&”

  5. Java反射【三、方法的反射】

    获取一个类下的所有方法 可以获取类类型后,获取到所有方法及相关信息 Method[] ms = c.getMethods(); 获取方法列表(public) Method[] ms = c.getDe ...

  6. vue项目中关于微信分享的坑,以及安卓和ios获取location.href不同的处理

    最近做vue项目的微信公众号项目,涉及到微信分享,记录一下心得,以备后用,vue路由用的是hash模式: 该项目只是公众号里面的h5链接,不需要获取code获取access_token的票据,因此前端 ...

  7. 【wifi移植 2】 移植wpa_supplicant

    参考文章: http://bbs.eeworld.com.cn/thread-447273-1-1.html(加精作品) 1. 下载源码 下载wpa_supplicant-2.2.tar(openss ...

  8. centos nginx https 配置

    1,如果想配置一个域名到指定目录咋弄呢?下面这个 server { listen ; server_name 这里换成你的域名,例如baidu.com; set $root_path '/home/w ...

  9. [AWS - EC2]如何使用 PuTTY 连接到 Amazon Linux 2 实例。How to Connect Amazon Linux 2 Instance from Windows Using PuTTY

    1. 下载 PuTTY 2. 安装到任意目录 3. 打开 PuTTYgen (注意不是 PuTTY), 如图: 选择 RSA , 点击 Load. 找到从 AWS 创建实例的时候生成的 key,要选择 ...

  10. gulp connect.static is not a function

    npm install --save serve-static var serveStatic = require('serve-static');