虽然内存的分配和回收技术已相当成熟,但如果需要排查内存溢出、内存泄露问题,或者要求高并发、高性能时,就需要对垃圾的回收进行监控和调节,以更好优化系统提高性能。

对象存活判定

Java 内存结构中,程序计数器、虚拟机栈、本地方法栈等随着线程而生,随线程而灭,不需要考虑内存回收问题。而 Java 堆和方法区则不同,它们的内存分配是动态的,只有在运行期间才能知道会创建哪些对象,垃圾回收关注的就是这两部分。

垃圾回收首先需要判断哪些对象还存活着,主要有引用计数和可达性分析两种算法。

引用计数算法

它的原理如下:给对象添加一个引用计数器,每当有一个地方引用它时,计时器值就加 1;当引用失效时,计数器值就减 1;如果计数器为 0,对象就不可能再被使用。

引用计数算法虽然实现简单、判定效率较高。但它很难解决对象之间循环引用的问题。

例如两个对象相互引用,实际上两个对象都不会再访问,但因为相互引用着对方,导致它们的计数器值都不为 0,于是引用技术算法无法通过 GC 收集器回收它们。

可达性分析算法

它的原理如下:通过一系列称为 GC Roots 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明对象是不可用的。

Java 中,可作为 GC Roots 的对象包括如下几种:

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

引用类型

可以看到,对象回收判定算法判断对象是否存活都与引用有关。从 JDK1.2 开始,引用分为四种类型,用来实现不同的功能,它们的引用强度也依次递减。

强引用(Strong Reference)

平时使用的引用就是强引用。只要强引用还存在,该对象永远不会被回收。

可以通过将对象设置为 null,使其被回收。

软引用(Soft Reference)

用于描述一些还有用但并非必需的对象。当系统内存空间不足时,会回收这些软引用指向的对象。它通过 SoftReference 类来实现软引用。

可以用来实现高速缓存。

弱引用(Weak Reference)

用来描绘非必需对象。被弱引用指向的对象只能生存到下一次垃圾回收之前。只要垃圾收集器运行,弱引用指向的对象就会被回收。它通过 WeakReference 类来实现弱引用。

虚引用(Phantom Reference)

虚引用和没有引用没有任何区别。一个对象是否有虚引用,不会影响其生存时间,也无法通过虚引用获取对象实例。它通过 PhantomReference 来实现虚引用。必须和引用队列 ReferenceQueue 联合使用。

为一个对象设置虚引用的唯一目的是该对象被垃圾收集器回收前会收到一条系统通知。

回收方法区

方法区,或者说 HotSpot 虚拟机中的永久代,进行垃圾回收的效率一般比较低。回收主要包括两部分内容:废弃常量和无用的类。

判断一个常量是否是废弃常量比较简单,与回收 Java 堆中的对象类似。而判定一个类是否是无用的类需要满足三个条件:

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

垃圾收集算法

标记-清除算法(Mark-Sweep)

标记-清除算法分为两个标记和清除阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。标记过程也就是对象存活判定算法。

它是最基础的收集算法,主要有两个缺点:

  • 效率问题:标记和清除两个过程的效率都不高。
  • 空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大的对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法(Copying)

复制算法将可用内存分为大小相等的两块,每次只使用其中的一块。在一块内存用完后,将仍存活的对象赋值到另一块上面,再把已使用过的内存一次清理掉。

复制算法的优缺点如下:

  • 优点:每次对半个分区进行内存回收,内存分配时也不用考虑内存碎片等情况,实现简单,运行高效。
  • 缺点:可使用的内存缩小为一半,代价较大。

标记-整理算法(Mark-compact)

标记-整理算法分为标记和整理两个阶段,标记阶段和“标记-清除算法”一样,但在整理阶段,不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

标记-整理算法的优缺点如下:

  • 避免了空间碎片,空间利用率较高。
  • 效率不高,标记和清除过程的效率较低。

分代算法(Generational Collection)

分代算法根据对象存活周期将内存划分为几块。一般是将 Java 对分为新生代和老年代,根据各个年代的特点采用适当的收集算法。

新生代中,每次垃圾收集时只有少量对象存活,选择复制算法;老年代中,对象存活率较高、没有额外空间进行分配,使用“标记-清理”或“标记-整理”算法。

为了对不同生命周期的对象采用不同的回收算法,所以垃圾收集器都采用分代收集算法,将堆分为新生代和老年代。

  

内存分配和回收策略

新生代

新生代主要用来存放新创建的对象,一般占堆 1/3 的空间。由于很多对象生命周期很短,每次 Minor GC 后只有少量对象存活,所以选用复制算法。

新生代又被分为一块较大的 Eden 区和两块较小的大小相等的 Survivor 区,使用 fromto 来分别指代两个 Survivor 区。HotSpot 虚拟机默认 Eden 和两块 Survivor 的大小比例为 8:1:1。每次只会使用 Eden 和其中一块 Survivor 区为对象服务,所以总是有一块 Survivor 区是空闲的,新生代实际可用的内存空间也就为 90%

通常,对象会分配在 Eden 区中,当 Eden 区无法在分配对象时,JVM 便会触发一次 Minor GC,将存活下来的对象复制到 from 指向的 Survivor 区中。

from 指向的 Survivor 区也无法分配时,对 Edenfrom 指向的 Survivor 区执行 Minor GC,将存活下来的对象复制到 to 指向的 Survivor 区中,然后交换 fromto 指针,使 to 指向的 Survivor 区为空,以保证下次 Minor GC 有复制的空闲空间。

老年代

老年代用于存放大对象,或年龄超过一定程度的对象。一般占据堆 2/3 的空间。

如果对象需要大量连续的内存空间,例如很长的字符串及数组,这些对象会直接分配在老年代,以避免在 Eden 区及两个 Survivor 区之间发生大量的内存复制。

虚拟机为每个对象定义了一个对象年龄计数器,如果对象分配在 Eden 区,在经过一次 Minor GC 后仍然存活,之后移动到 Survivor 空间中,将其年龄设置为 1。对象在 Survivor 区中每经过一次 Minor GC,年龄就增加一次,当它的年龄增加到一定程度(默认为 15)时,也会被晋升到老年代中。

如果在 Survivor 区中相同年龄所有对象大小的总和大于 Survivor 区的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。

老年代的对象一般都比较稳定,Major GC 不会频繁执行。Major GC 采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC 的耗时较长,而且会产生内存碎片。

三种清理方式

Minor GC(Young GC)

指发生在新生代的垃圾收集动作。当 Eden 区没有足够的空间分配时,就会触发一次 Minor GC。由于 Java 对象大多生命周期较短,所以 Minor GC 非常频繁,一般回收速度也比较快。

Major GC

指发生在老年代的垃圾收集动作,在进行 Major GC 前,一般都会进行至少一次 Minor GCMajor GC 的速度一般会比 Minor GC10 倍以上。

Full GC

指回收整个新生代和老年代的垃圾收集动作。成本较高,对系统性能产生影响。FULL GC 的时候会 STOP THE WORD

它的触发条件主要有:

  • 在执行 Minor GC 之前,如果老年代最大可用的连续空间小于历次晋升到老生代对象的平均大小,则触发一次 Full GC
  • 大对象直接进入老年代,或从年轻代晋升上来的老对象,在老年代尝试分配内存,但老年代内存空间不够时。
  • 显式调用 System.gc() 方法时。

参考资料

  • 雨点的名字:【JVM虚拟机】(2)---GC 算法与种类
  • Nutty:JVM的新生代、老年代、MinorGC、MajorGC

深入理解 JVM 之 垃圾回收机制相关推荐

  1. jvm垃圾回收机制_深入理解JVM的垃圾回收机制

    ​如何判断对象已"死" Java堆中存放着几乎所有的对象实例,垃圾回收器在堆进行垃圾回收前,首先要判断这些对象那些还存活,那些已经"死去".判断对象是否已&qu ...

  2. Java虚拟机(三)——初识JVM的垃圾回收机制

    前言 对于程序计数器.虚拟机栈.本地方法栈这三个部分而言,其生命周期与相关线程有关,随线程而生,随线程而灭.并且这三个区域的内存分配与回收具有确定性,因为当方法结束或者线程结束时,内存就自然跟着线程回 ...

  3. 细说JVM的垃圾回收机制

    什么是垃圾回收? 从字面看来,按字面意思来理解就是--找到垃圾对象并将他们抛弃掉:事实却正好相反,垃圾回收是把处于活动状态的对象找出来,而将剩余的对象标记为垃圾对象.基于此理论,我们来详细描述java ...

  4. 深入理解JVM(四)JVM的垃圾回收机制

    文章目录 1 什么是垃圾回收机制 2 Java中的引用类型 3 如何判断对象是否可以被回收 4 方法区的垃圾收集 5 垃圾收集算法 5.1 标记-清除(Mark-Sweep)算法 5.2 标记整理(M ...

  5. 安卓 java内存碎片_理解Android Java垃圾回收机制

    Jvm(Java虚拟机)内存模型 从Jvm内存模型中入手对于理解GC会有很大的帮助,不过这里只需要了解一个大概,说多了反而混淆视线. Jvm(Java虚拟机)主要管理两种类型内存:堆和非堆. 堆是运行 ...

  6. JVM的垃圾回收机制详解和调优

    1.JVM的gc概述 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都使用类似的 ...

  7. 理解Android Java垃圾回收机制

    讲GC原理很简单,但是把GC原理讲的简单就不简单了. 本文力求简单透彻,让所有Android开发人员都能明白基本的GC原理. Jvm(Java虚拟机)内存模型 从Jvm内存模型中入手对于理解GC会有很 ...

  8. jvm gc垃圾回收机制和参数说明amp;amp;Java JVM 垃圾回收(GC 在什么时候,对什么东西,做了什么事情)

    jvm gc(垃圾回收机制) Java JVM  垃圾回收(GC 在什么时候,对什么东西,做了什么事情) 前言:(先大概了解一下整个过程) 作者:知乎用户 链接:https://www.zhihu.c ...

  9. 【33】深入理解对象与垃圾回收机制

    (1)一个人只要自己不放弃自己,整个世界也不会放弃你. (2)天生我才必有大用 (3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟. (4)做难事必有所得 (5)精神乃真正的刀锋 ( ...

  10. 【JVM】垃圾回收机制及算法

    垃圾回收机制及算法 一.垃圾回收概述 二.对象是否存活 1. 判断对象是否存活 - 引用计数算法 2.判断对象是否存活-可达性分析算法 1.可达性分析算法 2.JVM之判断对象是否存活 3.关于引用 ...

最新文章

  1. PowerShell_5_零基础自学课程_5_自定义PowerShell环境及Powershell中的基本概念
  2. 90%的开发都不太考虑这个,但只要出问题直接公司完蛋!
  3. silverlight 安全性错误
  4. ICML 2020 | 基于连续动态系统学习更加灵活的位置编码
  5. 剑指offer 数值的整次方
  6. 二分查找非递归方式实现
  7. vCenter 升级错误 VCSServiceManager 1603
  8. SpringCloud 入门教程(八): 断路器指标数据监控Hystrix Dashboard 和 Turbine
  9. Mac OS/Linux命令查询网络端口占用情况
  10. mysqldump: Got error: 1016: Can't open file: './xxx.frm' (errno: 24) when using LOCK TABLES
  11. 华为修改优先级命令_华为配置命令大全
  12. 音乐 美术 劳技 计算机教研组工作总结,美术劳技室工作总结
  13. SparkSQL概念介绍
  14. 在树莓派上进行python编程_在树莓派上用Python控制LED
  15. Android基础:ViewPage
  16. 修改vscode图标
  17. Android清理缓存工具类
  18. Resolve operation not in progress, we are not resuming.
  19. 巧用网络分析仪的校准
  20. SUS评分提升了60%,我们做了什么?

热门文章

  1. java中jsp table标签属性_JSP自定义标签-属性
  2. java多个收银台收银_Java策略模式设计(简易收银台SpringBoot)
  3. 深度集成 Flink: Apache Iceberg 0.11.0 最新功能解读
  4. 看看别人后端API接口写得,那叫一个优雅!
  5. Ubuntu18.04 从头开始编译 Android Native WebRTC
  6. 面试官常问的线程池,你真的了解吗?
  7. Request中Attribute 和 Parameter 的区别
  8. python创建类mymath_构建DLL(MyMathFuncs)以在Python Ctypes中使用
  9. vivado中交织模块_Adalm Pluto SDR主动学习模块让您拥有完善的无线电RF射频实验室...
  10. android消息通知布局,Android Design