目录

常见垃圾收集策略

Reference Counting(引用计数)

跟踪收集器

Mark-Sweep Collector(标记-清除收集器)

Copying Collector(复制收集器)

Mark-Compact Collector(标记-整理收集器)

JVM 垃圾收集策略

Serial Collector(串行收集器)

Parallel Collector(并行收集器)

Concurrent Collector(并发收集器)

方法区的回收

垃圾分代回收算法(Generational Collecting)

JVM 垃圾回收工作运作流程


1、垃圾收集提供了内存管理的机制,使得应用程序不需要再关注内存如何释放,内存用完后,垃圾收集会自动进行收集,这样就减轻了因为人为的管理内存而造成的错误,比如在C++语言里,出现内存泄露时很常见的。

2、Java 语言是目前依赖于垃圾收集器最多的语言,但是垃圾收集器策略从 20 世纪 60 年代就已经流行起来了,比如Smalltalk, Eiffel 等编程语言也集成了垃圾收集器的机制。

3、程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收。垃圾回收主要是针对 Java 堆和方法区进行,因为一个 JVM 只有一个Java 堆和方法区。

常见垃圾收集策略

1、所有的垃圾收集算法都面临同一个问题,那就是找出应用程序不可达的内存块,将其释放,这里面得不可达主要是指应用程序已经没有内存块的引用了。在 JAVA 中,某个对象对应用程序可达是指:这个对象被根(根主要是指类的静态变量,或者活跃在所有线程栈的对象的引用)引用或者对象被另一个可到达的对象引用。

Reference Counting(引用计数)

1、引用计数是最简单直接的一种方式,这种方式在每一个对象中增加一个引用的计数,这个计数代表当前程序有多少个引用引用了此对象,如果此对象的引用计数变为 0,那么此对象就可以作为垃圾收集器的目标对象来收集。

2、优点:简单,直接,不需要暂停整个应用。

3、缺点:1)需要编译器的配合,编译器要生成特殊的指令来进行引用计数的操作,比如每次将对象赋值给新的引用,或者者对象的引用超出了作用域等。2)不能处理循环引用的问题

跟踪收集器

1、跟踪收集器首先要暂停整个应用程序,然后开始从根对象扫描整个堆,判断扫描的对象是否有对象引用。

2、如果每次扫描整个堆,那么势必让 GC 的时间变长,从而影响了应用本身的执行,因此在 JVM 里面采用了分代收集,在新生代收集的时候 minor gc 只需要扫描新生代,而不需要扫描老生代。minor(少数的)。

3、JVM 采用了分代收集以后,minor gc 只扫描新生代,但是 minor gc 怎么判断是否有老生代的对象引用了新生代的对象,JVM采用了卡片标记的策略,卡片标记将老生代分成了一块一块的,划分以后的每一个块就叫做一个卡片,JVM 采用卡表维护了每一个块的状态,当 JAVA 程序运行的时候,如果发现老生代对象引用了新生代对象,那么就 JVM 就将卡表的状态设置为脏状态,这样每次 minor gc 的时候就会只扫描被标记为脏状态的卡片,而不需要扫描整个堆。具体如下图:

4、GC 在收集一个对象的时候会判断是否有引用指向对象,在 JAVA 中的引用主要有四种:Strong reference、Soft reference、Weak reference、Phantom reference.

Strong Reference(强引用)

1、强引用是 JAVA 中默认采用的一种方式,平时创建的引用都属于强引用,只要强引用存在,垃圾回收器永远不会回收被引用的对象,平时 new 出来的对象的是创建强引用。

public class HelloWorld {public static void main(String[] args) throws InterruptedException {HelloWorld helloWorld = new HelloWorld();//helloWorld 就是强引用}
}

Soft Reference(软引用)

1、软引用的对象在 GC 的时候不会被回收,只有当内存不够用的时候才会真正的回收,因此软引用适合缓存的场合,这样使得缓存中的对象可以尽量的再内存中待长久一点。

import java.lang.ref.SoftReference;
public class HelloWorld {public static void main(String[] args) throws InterruptedException {String str = "test";SoftReference<String> softreference = new SoftReference<>(str);//创建给定对象的新的软引用,新引用未注册到任何队列}
}

java.lang.ref.SoftReference:此类的直接实例可用于实现简单的缓存; 此类或派生子类也可用于较大的数据结构以实现更复杂的高速缓存。

Weak reference(弱引用)

1、弱引用有利于对象更快的被回收,假如一个对象只有弱引用,那么在 GC 后,这个对象肯定会被回收。

import java.lang.ref.WeakReference;
public class HelloWorld {public static void main(String[] args) throws InterruptedException {String str = "test";WeakReference<String> softreference = new WeakReference<>(str);//创建给定对象的新的弱引用}
}

java.lang.ref.WeakReference:弱引用对象,最常用于实现规范化映射。

Phantom reference.(虚引用)

1、又称为幽灵引用或者幻影引用。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用取得一个对象实例。

2、为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

3、可以使用虚引用对象 PhantomReference 来实现虚引用。

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);

Mark-Sweep Collector(标记-清除收集器)

1、标记清除收集器最早由 Lisp 的发明人于 1960 年提出,标记清除收集器停止所有的工作,从根扫描每个活跃的对象,然后标记扫描过的对象,标记完成以后,清除那些没有被标记的对象。

2、优点:1)解决循环引用的问题。2)不需要编译器的配合,从而就不执行额外的指令

3、缺点:每个活跃的对象都要进行扫描,收集暂停的时间比较长。会产生大量碎片,内存碎片过多可能导致无法给大对象分配内存

Copying Collector(复制收集器)

1、复制收集器将内存分为两块一样大小空间,某一个时刻,只有一个空间处于活跃的状态,当活跃的空间满的时候,GC 就会将活跃的对象复制到未使用的空间中去,原来不活跃的空间就变为了活跃的空间。

2、优点:只扫描可以到达的对象,不需要扫描所有的对象,从而减少了应用暂停的时间

3、缺点:1)需要额外的空间消耗,某一个时刻,总是有一块内存处于未使用状态。2)复制对象需要一定的开销

Mark-Compact Collector(标记-整理收集器)

1、标记整理收集器汲取了标记清除和复制收集器的优点,它分两个阶段执行,在第一个阶段,首先扫描所有活跃的对象,并标记所有活跃的对象,第二个阶段首先清除未标记的对象,然后将活跃的的对象复制到堆得底部。

2、Mark-compact 策略极大的减少了内存碎片,并且不需要像 Copy Collector一样需要两倍的空间。

JVM 垃圾收集策略

1、GC 执行时要耗费一定的 CPU 资源和时间,因此在 JDK1.2 以后,JVM 引入了分代收集的策略,其中对新生代采用 "Mark-Compact" 策略(标记整理策略),而对老生代采用了“Mark-Sweep"策略(标记清除策略)。

2、其中新生代的垃圾收集器命名为 “minor gc”,老生代的 GC 命名为 "Full Gc 或者Major GC"。用 System.gc() 强制执行的就是Full Gc.

Serial Collector(串行收集器)

1、Serial Collector 是指任何时刻都只有一个线程进行垃圾收集,这种类型的收集器适合于单CPU的机器。它需要停止整个应用的执行。

Serial Copying Collector

1、此种 GC 用 -XX:UseSerialGC 选项配置,它只用于新生代对象的收集。

2、JDK 1.5 以后 -XX:MaxTenuringThreshold 设置对象复制的次数。当 eden 空间不够的时候,GC会将eden的活跃对象和一个名叫From survivor空间中尚不够资格放入Old代的对象复制到另外一个名字叫To Survivor的空间。而此参数就是用来说明到底From survivor中的哪些对象不够资格,假如这个参数设置为31,那么也就是说只有对象复制31次以后才算是有资格的对象。

1)From Survivor 和 To survivor 的角色是不断的变化的,同一时间只有一块空间处于使用状态,这个空间就叫做 From Survivor 区,当复制一次后角色就发生了变化。
2)如果复制的过程中发现 To survivor 空间已经满了,那么就直接复制到old generation.
3)比较大的对象也会直接复制到 Old generation,在开发中,应该尽量避免这种情况的发生。

Serial Mark-Compact Collector

1、串行的标记-整理收集器是 JDK5 update6 之前默认的老生代的垃圾收集器,此收集使得内存碎片最少化,但是它需要暂停的时间比较长。

Parallel Collector(并行收集器)

1、Parallel Collector 主要是为了应对多CPU,大数据量的环境。

2、Parallel Collector 又可以分为以下两种:

1)Parallel Copying Collector:此种 GC 用 -XX:UseParNewGC 参数配置,它主要用于新生代的收集,此GC可以配合CMS一起使用。
2)Parallel Mark-Compact Collector:此种 GC 用 -XX:UseParallelOldGC 参数配置,此GC主要用于老生代对象的收集。1.6.0。

3、Parallel scavenging Collector:此种 GC 用 -XX:UseParallelGC 参数配置,它是对新生代对象的垃圾收集器,但是它不能和CMS配合使用,它适合于比较大新生代的情况,此收集器起始于jdk 1.4.0。它比较适合于对吞吐量高于暂停时间的场合。

4、Serial gc和Parallel gc可以用如下的图来表示:

Concurrent Collector(并发收集器)

1、Concurrent Collector 通过并行的方式进行垃圾收集,这样就减少了垃圾收集器收集一次的时间,这种GC在实时性要求高于吞吐量的时候比较有用。

2、此种GC可以用参数-XX:UseConcMarkSweepGC配置,此GC主要用于老生代和Perm代的收集。

方法区的回收

1、因为方法区主要存放永久代对象,而永久代对象的回收率比新生代差很多,因此在方法区上进行回收性价比不高。

2、主要是对常量池的回收和对类的卸载。

3、类的卸载条件很多,需要满足以下三个条件,并且满足了也不一定会被卸载:

1)该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。

2)加载该类的 ClassLoader 已经被回收。

3)该类对应的 java.lang.Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。

4、可以通过 -Xnoclassgc 参数来控制是否对类进行卸载。

5、在大量使用反射、动态代理、CGLib 等 ByteCode 框架、动态生成 JSP 以及 OSGo 这类频繁自定义 ClassLoader 的场景都需要虚拟机具备类卸载功能,以保证不会出现内存溢出。

垃圾分代回收算法(Generational Collecting)

1、分代回收算法是基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述3代中的一个)进行回收。

2、现在 sum jvm 的垃圾回收器(从J2SE1.2开始)都是使用此算法。

Young(年轻代) 1)Young(年轻代)是 JVM specification 中 Heap的一部份。
2)年轻代分三个区。一个 Eden(伊甸园)区,两个 Survivor(幸存者)区。
3)大部分对象在 Eden 区中生成,当 Eden 区已满时,还存活的对象将被复制到 Survivor区(两个中的一个),当这个 Survivor 区已满时,此区的存活对象将被复制到另外一个Survivor 区,当这个 Survivor 区也满了的时候,从第一个 Survivor 区复制过来的并且此时还存活的对象,将被复制 "年老区"。
4)需要注意 Survivor 的两个区是对称平等的,没先后关系,任何时候 Survivor 区总有一个是空的。
Tenured(年老代) 1)Tenured(年老代)是 JVM specification 中 Heap 的一部份 
2)年老代存放从年轻代存活的对象,一般来说年老代存放的都是生命期较长的对象
Perm(持久代)  1)Perm(Permanent 持久代) 是 JVM specification 中的 Method area 用于存放静态文件,如今静态 Java 类、静态方法、静态成员变量等。
2)持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些 class,例如 Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。
3)持久代大小通过 -XX:MaxPermSize= 进行设置。

JVM 垃圾回收工作运作流程

1、首先当启动 J2EE 应用服务器时,JVM 随之启动,并将 JDK 的类和接口,应用服务器运行时需要的类和接口,以及类和接口中的定义文件,还要编译后的 Class 文件或 JAR 包中的 Class 文件装载到 JVM 的永久存储区。

2、在伊甸园中创建应用服务器运行时必须的 JAVA 对象,创建 J2EE 应用启动时必须创建的JAVA对象,J2EE 应用启动完毕,可对外提供服务。 JVM 在伊甸园区根据用户的每次请求创建相应的 JAVA 对象。

3、当伊甸园的空间不足以用来创建新 JAVA 对象的时候,JVM 的垃圾回收器执行对伊甸园区的垃圾回收工作,销毁那些不再被其他对象引用的 JAVA 对象(如果该对象仅仅被一个没
有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并将那些被其他对象所引用的JAVA对象移动到幸存者0区。

4、如果幸存者0区有足够空间存放,则直接放到幸存者0区,如果幸存者0区没有足够空间存放,则 JVM 的垃圾回收器执行对幸存者 0 区的垃圾回收工作,销毁那些不再被其他对象引用的JAVA对象(如果该对象仅仅被一个没有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并将那些被其他对象所引用的 JAVA 对象移动到幸存者1区。

5、如果幸存者1区有足够空间存放则直接放到幸存者1区;如果幸存者1区没有足够空间存放,则JVM的垃圾回收器执行对幸存者1区的垃圾回收工作,销毁那些不再被其他对象引用的 JAVA 对象(如果该对象仅仅被一个没有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并将那些被其他对象所引用的 JAVA 对象移动到养老区。

6、如果养老区有足够空间存放则直接放到养老区,如果养老区没有足够空间存放,则JVM的垃圾回收器执行对养老区区的垃圾回收工作,销毁那些不再被其他对象引用的JAVA对象(如果该对象仅仅被一个没有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并保留那些被其他对象所引用的JAVA对象。

7、如果到最后养老区,幸存者1区,幸存者0区和伊甸园区都没有空间的话,则JVM会报告 "JVM堆空间溢出(java.lang.OutOfMemoryError: Java heap space)",表示在堆空间没有空间再来创建对象。

8、这就是 JVM 的内存分区管理,相比不分区来说,一般情况下,垃圾回收的速度要快很多;因为在没有必要的时候不用扫描整片内存而节省了大量时间。 通常大家还会遇到另外一种内存溢出错误 "永久存储区溢出(java.lang.OutOfMemoryError: Java Permanent Space)"。

Java 垃圾收集策略、垃圾分代回收算法、垃圾回收运作流程相关推荐

  1. 欧尼酱讲JVM(22)——分代收集算法

    目录 分代收集算法 HotSpot中的分代收集 年轻代 老年代 没有一种最好的算法吗?没有,没有最好只有最适合.具体问题具体分析! 上一篇文章<欧尼酱讲JVM(21)--垃圾回收相关算法> ...

  2. 2、垃圾回收算法(标记清除算法、复制算法、标记整理算法和分代收集算法),各种垃圾收集器讲解(学习笔记)

    2.垃圾回收概述 2.1.垃圾回收算法 2.1.1.垃圾回收算法-标记清除算法 2.1.2.垃圾回收算法–复制算法 2.1.3.垃圾回收算法–标记整理算法和分代收集算法 2.1.4.垃圾回收算法–Se ...

  3. 【Android 内存优化】内存抖动 ( 垃圾回收算法总结 | 分代收集算法补充 | 内存抖动排查 | 内存抖动操作 | 集合选择 )

    文章目录 一. 垃圾回收算法总结 二. 分代收集算法补充 三. 查看 Java 虚拟机 四. 获取 Android 应用可使用最大内存 五. 内存抖动标志 六. 排查内存抖动 七. 常见的造成内存抖动 ...

  4. 【Android 内存优化】垃圾回收算法 ( 分代收集算法 | Serial 收集器 | ParNew 收集器 | Parallel Scavenge 收集器 | CMS 并发标记清除收集器 )

    文章目录 一. 分代收集算法 二. 垃圾回收器 / 收集器 ( GC ) 三. 串行收集器 ( Serial ) 四. ParNew 收集器 五. Parallel Scavenge 收集器 六. C ...

  5. 26 Java GC算法 垃圾收集器、标记 -清除算法、复制算法、标记-压缩算法、分代收集算法

    26.Java GC算法 垃圾收集器 1.1.1标记 -清除算法 1.1.2复制算法 1.1.3标记-压缩算法 1.1.4分代收集算法 26.Java GC算法 垃圾收集器 概述 垃圾收集 Garba ...

  6. JVM学习-分代收集算法

    分代收集算法 1.分代回收算法概述 1.1.分代回收算法简介 1.2.回收流程 1.3.回收流程总结 2.相关VM参数 3.GC分析 1.分代回收算法概述 1.1.分代回收算法简介 我们在前面讲了三种 ...

  7. JVM内存管理------GC算法精解(五分钟教你终极算法---分代搜集算法)

    转载自   JVM内存管理------GC算法精解(五分钟教你终极算法---分代搜集算法) 引言 何为终极算法? 其实就是现在的JVM采用的算法,并非真正的终极.说不定若干年以后,还会有新的终极算法, ...

  8. php垃圾回收算法分代,PHP的垃圾回收机制代码实例讲解

    PHP可以自动进行内存管理,清除不需要的对象,主要使用了引用计数 在zval结构体中定义了ref_count和is_ref , ref_count是引用计数 ,标识此zval被多少个变量引用 , 为0 ...

  9. 垃圾分代回收机制简单介绍

    针对GC的简单介绍 JVM对自己的内存进行了划分5个区域,分别是堆,栈,方法区,本地方法栈,程序计数器.Java中对每一种类型都规定了具体的不可变的大小.所以所有的内存都是由JVM自动分配,所有的内存 ...

  10. java 8的内存分代改进_java8的内存结构,这一篇文章就够了

    在一开始学习java的时候,那时候是在网上看视频,老师就经常提到什么对象分配在堆区,什么在栈区,那时候和理解,后来理解了就想着写一篇文章好好的去梳理一下. 想说一下这篇文章的脉络: 首先,研究java ...

最新文章

  1. 员工培训:如何制定以数据为依据的业务决策
  2. 2022图神经网络5篇最新的研究综述:双曲/图分类/联邦/等变/异质性
  3. 【温故知新】HTML学习笔记(上)
  4. JavaScript:向数组开头添加
  5. 测试人员如何使用浏览器的f12_测试过程中如何快速定位一个bug
  6. 【参考】微信 - 数据库 -官方封装接口说明:
  7. Java-NIO实战多人聊天室
  8. java经典英文面试题,Java-英文面试题-经典
  9. Hudson Jameson将在柏林硬分叉后卸任以太坊基金会社区经理
  10. 医院大数据中心建设要点分析
  11. AC日记——[HNOI2012]永无乡 bzoj 2733
  12. oracle实际是什么意思,Oracle遇到的应用实际教程
  13. SQL Server中默认的数据库及作用
  14. 通过经纬度求解方位角
  15. 抽象代数笔记2——群
  16. WERTYU - UVA - 10082
  17. 疫情让“灵活用工”浮出水面,作为Android开发的你“灵活用工”了吗?
  18. BGP进阶:BGP 综合实验一
  19. 中国各阶级收入统计表,看看你在哪个阶级
  20. n个评委给m个选手打分python_n个评委为m个选手打分(n个评委打分总次数mn)。请问如何评判m个选手的成绩?...

热门文章

  1. 用Python解决简单的水果分类问题(二)
  2. 孙鑫VC学习笔记:第十四讲 (一) 网络的基本概念
  3. 孙鑫VC学习笔记:第九讲 界面修改,工具栏,状态栏,启动画面的制作
  4. (10)Python----numpy.hstack
  5. php数组由哪三部分构成,数据结构研究的主要内容有哪三部分
  6. java io 读取配置文件_(转)Java 读写Properties配置文件
  7. APIcloud解决检出到指定路径:false问题
  8. Gensim进阶教程
  9. java当前类路径_Java取得当前类的路径
  10. github 如何 只下载 一个项目中的 部分 代码文件