文章目录

  • 1 什么是垃圾回收机制
  • 2 Java中的引用类型
  • 3 如何判断对象是否可以被回收
  • 4 方法区的垃圾收集
  • 5 垃圾收集算法
    • 5.1 标记-清除(Mark-Sweep)算法
    • 5.2 标记整理(Mark-Compact)算法
    • 5.3 复制算法
    • 5.4 分代收集算法
  • 6 内存分配与回收策略
    • 6.1 内存分配策略
      • 6.1.1 内存分配
      • 6.1.2 堆的年轻代为什么要有两个Survivor区
    • 6.2 内存回收策略
      • 6.2.1 Minor GC 、Major GC、Mixed GC和 Full GC的区别
      • 6.2.2 Minor GC触发条件
      • 6.2.3 Full GC触发条件

1 什么是垃圾回收机制

垃圾回收(Garbage Collection)是JVM垃圾回收器提供的一种用于死去的(不可能再被任何途径使用的)对象占据的内存空间的一种机制。

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

2 Java中的引用类型

Java里有不同的引用类型,分别是强引用、软引用、弱引用和虚引用。

  • 强引用: 无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象
  • 软引用: SoftReference内存充足时不回收,内存不足时则回收
  • 弱引用: WeakReference 不管内存是否充足,只要GC一运行就会回收该引用对象
  • 虚引用: PhantomReference是最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。
//强引用
Object object = new Object();
//软引用
SoftReference softReference=new SoftReference<>("1");
//弱引用
WeakReference weakReference=new WeakReference<>("2");
//虚引用
PhantomReference phantomReference = new PhantomReference("3",new ReferenceQueue());

3 如何判断对象是否可以被回收

在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对象)了。

判断对象是否存活一般有两种方式:
引用计数:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一,计数为0时可以回收。此方法虽然简单,无法解决对象相互循环引用的问题。比如两个对象已经不可能再被访问,但是它们因为互相引用着对方,导致它们的引用计数都不为零,引用计数算法也就无法回收它们。
可达性分析:通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。

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

在 Java 中 GC Roots 一般包含以下内容:

  • 虚拟机栈中引用的对象
  • 本地方法栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中的常量引用的对象

4 方法区的垃圾收集

方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。假如一个字符串“java”曾经进入常量池中,但是当前系统又没有任何一个字符串对象的值是“java”,换句话说,已经没有任何字符串对象引用常量池中的“java”常量,且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收,而且垃圾收集器判断确有必要的话,这个“java”常量就将会被系统清理出常量池。

5 垃圾收集算法

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

首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。

缺点:

  • 执行效率不稳定

如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低。

  • 内存空间的碎片化问题

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

5.2 标记整理(Mark-Compact)算法

首先标记出所有需要回收的对象,在标记完成后,让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

缺点:
如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行,像这样的停顿被描述为"Stop The World"。

5.3 复制算法

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

缺点:
可用内存缩小为了原来的一半,空间浪费太多

5.4 分代收集算法

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

在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。

6 内存分配与回收策略

6.1 内存分配策略

6.1.1 内存分配

  • 对象优先在 Eden 分配。大多数情况下,对象在新生代 Eden 区分配,当 Eden 区空间不够时,发起 Minor GC。
  • 大对象直接进入老年代。大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。
  • 长期存活的对象进入老年代。对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄(默认是15,可以使用-XX:MaxTenuringThreshold 参数来进行设置)则移动到老年代中。
  • 动态对象年龄判定。Survivor区的对象年龄从小到大进行累加,当累加到X年龄时的总和大于50%(可以使用-XX:TargetSurvivorRatio 参数来进行设置),那么比年龄X大的对象都会晋升到老年代,不必达到MaxTenuringThreshold 中要求的年龄。
  • 空间分配担保。在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果大于,那么就进行 Minor GC 。 如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那么就要进行一次 Full GC。

6.1.2 堆的年轻代为什么要有两个Survivor区

  • 如果没有Survivor区,每触发一次Minor GC,就会把Eden区的对象复制到老年代,这样当老年代满了之后会触发Major GC/Full GC(通常伴随着MinorGC),比较耗时,所以必须有Survivor区;
  • 如果只有一个survivor区,刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor
    GC,Eden中的存活对象就会被移动到Survivor区。下一次Eden满了的时候,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。
  • 如果有两个survivor区,新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1,S0和Eden被清空,然后下一轮S0与S1交换角色,如此循环往复,这就解决了内存碎片化的问题。

6.2 内存回收策略

6.2.1 Minor GC 、Major GC、Mixed GC和 Full GC的区别

  • 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
  • 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。“Major GC”这个说法现在有点混淆,在不同资料上常有不同所指,需按上下文区分到底是指老年代的收集还是整堆收集。
  • 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。
  • 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。

6.2.2 Minor GC触发条件

当Eden区满时,触发Minor GC,而Survivor区满不会触发Minor GC。

6.2.3 Full GC触发条件

  • System.gc()方法的调用(此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存)
  • 老年代空间不足
  • 空间分配担保失败
  • 方法区空间不足
  • 由 Eden 区、S0 区向 S1 复制的对象太大,导致直接进入老年代,但是老年代内存不够
  • CMS GC时出现concurrent mode failure(执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足,便会报 Concurrent Mode Failure 错误,并触发 Full GC)

深入理解JVM(四)JVM的垃圾回收机制相关推荐

  1. 详解JVM内存管理与垃圾回收机制2 - 何为垃圾

    随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背后的无名英雄,GC离我们的工作似乎越来越远.作为Java程序员,对这一点也许会有更深的体会,我们不需要了解太多与GC相关的知识,就能很 ...

  2. 详解JVM内存管理与垃圾回收机制5 - Java中的4种引用类型

    在Java语言中,除了基础数据类型的变量以外,其他的都是引用类型,指向各种不同的对象.在前文我们也已经知道,Java中的引用可以是认为对指针的封装,这个指针中存储的值代表的是另外一块内存的起始地址(对 ...

  3. JVM原理(Java代码编译和执行的整个过程+JVM内存管理及垃圾回收机制)

    转载注明出处: http://blog.csdn.net/cutesource/article/details/5904501 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.e ...

  4. JVM架构和GC垃圾回收机制--面试

    JVM架构和GC垃圾回收机制详解 JVM架构图分析 下图:参考网络+书籍,如有侵权请见谅 (想了解Hadoop内存溢出请看: Hadoop内存溢出(OOM)分类.参数调优化) JVM被分为三个主要的子 ...

  5. 《深入理解Java虚拟机》阅读——垃圾回收机制

    <深入理解Java虚拟机>阅读--垃圾回收机制 前言 why--为什么需要垃圾回收 what--垃圾回收做些什么 where--去哪里回收垃圾 how--垃圾回收是怎么做的 垃圾是否要回收 ...

  6. 深入理解JVM虚拟机(二):垃圾回收机制

    谈起GC,应该是让Java程序员最激动的一项技术,我相信每个Java程序员都有探究GC本质的冲动!JVM垃圾回收机制对于了解对象的创建和对象的回收极为重要,是每个Java程序员必须掌握的技能. 本博客 ...

  7. 浅谈JVM的实现与垃圾回收机制

    Java被称为是一个人类可读的编程语言,其主要特点是基于类和面向对象,Java的开源版本被称为OpenJDK.Java编程环境由两个部分组成:Java语言和运行环境,运行环境也称为Java虚拟机(JV ...

  8. JVM (二) 垃圾回收机制概念+垃圾回收器种类

    前言 做一个有趣的程序员.哈哈哈哈 本次铁村的小蓝猫主要给大家详细分享JVM中垃圾回收机制 学习JVM 肯定是要了解垃圾回收机制的. 分享前,我们先了解下本次分享内容的框架. 一.垃圾回收机制定义 1 ...

  9. 深入理解Java虚拟机:Java垃圾回收机制

    本篇内容包括:JAVA 垃圾回收机制概述.有哪些内存需要回收.如何回收(标记-清除.标记-整理(标记-清除-压缩).复制(标记-复制-清除).分代收集等算法) 以及 何时进行垃圾回收等内容! 一.概述 ...

  10. Java深度历险(四)——Java垃圾回收机制与引用类型

    Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对 ...

最新文章

  1. 条件独立(conditional independence) 结合贝叶斯网络(Bayesian network) 概率有向图 (PRML8.2总结)...
  2. oracle+SQL优化实例
  3. 剑指offer 数值的整次方
  4. ruby中的回调方法和钩子方法
  5. mysql数据库连接_mysql数据库连接池配置教程
  6. 带你玩转七牛云存储——高级篇
  7. UI设计插画素材|移动设备和网络屏幕
  8. Power Query获取整理多来源数据
  9. Android存储-SharedPreferences
  10. 【笔记】西门子1200PLC和V90伺服电机连接
  11. 微博html5版登录网址,微博网页版登录入口
  12. SVN忽略指定文件或文件夹的提交
  13. Android设备上px(像素)、dpi(像素密度)、dip(密度无关像素)之间的关系
  14. Python中NaN的处理
  15. 在 Windows Service 服务上部署 AutoVue_EMP_21_0_2 的操作手册
  16. cupy利用GPU来加速你的numpy操作
  17. 2023年软考考试时间及相关安排
  18. LeetCode(89)GrayCode
  19. C# 编写Word文档
  20. 蔡徐坤1亿转发量幕后推手“星援app”被端

热门文章

  1. 演讲实录丨山世光 闲话AI时代的视觉智能
  2. android x8,3英寸高性价比Android手机 索爱X8评测
  3. check-蓝鲸CTF hta,VMP
  4. 巴黎不哭!十亿数据精准扫描,帮卡西莫多重新找回他的玫瑰花窗
  5. 【网络科普】Wi-Fi 安全接入方法 - WEP, WPA, WPS
  6. 基于matlab的水塔水位控制设计,水箱液位控制系统设计
  7. oracle对数据块的存取,简单了解数据在Oracle文件中的存储
  8. CSDN换了新的logo
  9. crt打开FTP文件服务器,crt登陆到ftp服务器
  10. python pyWinhook的安装,python3完美替代pyhook/3