目录

  • 3.2 对象已死吗

    • 判断一个对象是否可被回收
    • 引用类型
    • finalize()
    • 回收方法区
  • 3.3. 垃圾收集算法
    • 1.Mark-Sweep(标记-清除)算法
    • 2.Copying(复制)算法
    • 3.Mark-Compact(标记-整理)算法
    • 4.Generational Collection(分代收集)算法
  • 3.5 垃圾收集器
    • 1.Serial
    • 2.ParNew
    • 3.Parallel Scavenge
    • 4.Serial Old
    • 5.Parallel Old
    • 6.CMS
    • 7.G1
  • 3.6 内存分配与回收策略
    • Minor GC 和 Full GC
    • 内存分配策略
    • GC触发条件

3.2 对象已死吗

判断一个对象是否可被回收

  • 1.引用计数法

对堆中每个对象添加一个引用计数器;当对象被引用时,引用计数器加1;当引用被置为空或离开作用域时,引用计数减1。简单但效率低,无法解决相互引用的问题。

public class ReferenceCountingGC {public Object instance = null;public static void main(String[] args) {ReferenceCountingGC objectA = new ReferenceCountingGC();ReferenceCountingGC objectB = new ReferenceCountingGC();objectA.instance = objectB;objectB.instance = objectA;}
}
  • 2.可达性分析算法(根搜索算法)

利用JVM维护的对象引用图,从根节点( GC Roots)开始遍历对象的引用图,同时标记遍历到的对象。当遍历结束后,未被标记的对象就是目前已不被使用的对象,可以被回收了。在Java语言中,可作为GC Roots的对象包括下面几种:

  1. 虚拟机栈中引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中JNI引用的对象

引用类型

  • 1.强引用

被强引用关联的对象不会被回收。

使用 new 一个新对象的方式来创建强引用。

Object obj = new Object();2.

  • 2.软引用

被软引用关联的对象只有在内存不够的情况下才会被回收。

使用 SoftReference 类来创建软引用。

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;  // 使对象只被软引用关联
  • 3.弱引用

被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前。

使用 WeakReference 类来实现弱引用。

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;  // 使对象只被弱引用关联
  • 4.虚引用

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

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

使用 PhantomReference 来实现虚引用。

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj = null;  // 使对象只被虚引用关联

finalize()

如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法:

  1. 当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。此对象将被回收。
  2. 如果这个对象被判定为有必要执行finalize()方法【重写了finalize()方法,且该方法没有被调用过】,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、 低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。

finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

/*** 此代码演示了2点:* 1.对象可以在被GC时自我拯救。* 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次*/
public class FinalizeEscapeGC {public static FinalizeEscapeGC SAVE_HOOK = null;public void isAlive() {System.out.println("yes,i am still alive:)");}@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("finalize mehtod executed!");FinalizeEscapeGC.SAVE_HOOK = this;}public static void main(String[] args) throws InterruptedException {SAVE_HOOK = new FinalizeEscapeGC();// 对象第一次成功拯救自己SAVE_HOOK = null;System.gc();// 因为finalize方法优先级很低,所以暂停0.5秒以等待它Thread.sleep(500);if (SAVE_HOOK != null) {SAVE_HOOK.isAlive();} else {System.out.println("no,i am dead:(");}// 下面这段代码与上面的完全相同,但是这次自救却失败了SAVE_HOOK = null;System.gc();Thread.sleep(500);if (SAVE_HOOK != null) {SAVE_HOOK.isAlive();} else {System.out.println("no,i am dead:(");}}
}Console:
finalize mehtod executed!
yes,i am still alive:)
no,i am dead:(

回收方法区

永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。类需要同时满足下面3个条件才能算是“无用的类”:

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

3.3. 垃圾收集算法

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

这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。

2.Copying(复制)算法

为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。

3.Mark-Compact(标记-整理)算法

该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。虽然可以大大简化消除碎片的工作,但是每次处理都会带来性能的损失。

4.Generational Collection(分代收集)算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

  • 新生代使用:复制算法
  • 老年代使用:标记 - 清除 或 标记 - 整理 算法

3.5 垃圾收集器

1.Serial

Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程,直到垃圾收集结束。Serial收集器是针对新生代的收集器,采用的是Copying算法,它简单高效,对于限定单个CPU环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,因此Serial垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。

2.ParNew

ParNew收集器是Serial收集器的多线程版本,也使用Copying算法 ,除了使用多个线程进行垃圾收集外,其余的行为和Serial收集器完全一样。ParNew收集器默认开启和CPU数目相同的线程数,可以通过-XX:ParallelGCThreads参数来限制垃圾收集器的线程数。ParNew垃圾收集器是很多java虚拟机运行在Server模式下新生代的默认垃圾收集器。

3.Parallel Scavenge

Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。高吞吐量可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。

4.Serial Old

Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。在Server模式下,主要有两个用途:a.在JDK1.5之前版本中与新生代的Parallel Scavenge收集器搭配使用。b.作为年老代中使用CMS收集器的后备垃圾收集方案。

5.Parallel Old

Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程的Mark-Compact算法。

6.CMS

CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。

7.G1

G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。采用Mark-Compact算法,因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

3.6 内存分配与回收策略

Minor GC 和 Full GC

  • 新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。
  • 老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。 Major GC的速度一般会比Minor GC慢10倍以上。

内存分配策略

  • 1.对象优先在Eden区分配

对象在新生代Eden区中分配,当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。

  • 2.大对象直接在老年代中分配

大对象直接在老年代中分配,JVM提供参数设置 –XX:PretenureSizeThreshold,当对象需要的空间大于该值时,直接在老年代分配(目的:避免在Eden和Survivor区之间发生大量的copy,新生代GC采用复制算法)。

  • 3.长期存活的对象进入老年代

长期存活的对象将进入老年代,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1。对象在Survivor区中每熬过一次MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁)时,就会被晋升到老年代中。-XX:MaxTenuringThreshold 用来定义年龄的阈值

  • 4.动态对象年龄判定

虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄≥该年龄的对象就可以直接进入老年代。

  • 5.空间分配担保

在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那么就要进行一次 Full GC。

GC触发条件

Minor GC:其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。

Full GC:相对复杂,有以下条件:

  • 1.调用 System.gc()

只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。

  • 2.老年代空间不足

老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。

为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。

  • 3.空间分配担保失败

使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。

  • 4.JDK 1.7 及以前的永久代空间不足

在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。

当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。

为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。

  • 5.Concurrent Mode Failure

执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。

转载于:https://www.cnblogs.com/zengzhihua/p/9708528.html

深入理解Java虚拟机:垃圾收集器与内存分配策略相关推荐

  1. 深入理解Java虚拟机——垃圾收集器与内存分配策略

    文章目录 对象已死? 引用计数算法 可达性分析算法 再谈引用 强引用: 软引用: 弱引用: 虚引用: 回收方法区 垃圾收集算法 标记 - 清除算法 缺点: 标记 - 复制算法 标记 - 整理算法 分代 ...

  2. 读书笔记——Java虚拟机垃圾收集器与内存分配策略

    本文章已授权微信公众号郭霖(guolin_blog)转载. 本文章讲解的内容是Java虚拟机垃圾收集器与内存分配策略. 概述 说起垃圾收集(Garbage Collection),也就是GC,大部分人 ...

  3. 深入理解Java虚拟机-垃圾回收器与内存分配策略

    本博客主要参考周志明老师的<深入理解Java虚拟机>第二版 读书是一种跟大神的交流.阅读<深入理解Java虚拟机>受益匪浅,对Java虚拟机有初步的认识.这里写博客主要出于以下 ...

  4. JAVA经典垃圾收集器与内存分配策略

    目录 判断对象存活状态 引用计数算法 可达性分析算法 再谈引用 finalize()方法 回收方法区 垃圾收集算法--追踪式垃圾收集 分代收集理论 标记-清除算法 标记-复制算法 标记-整理算法 Ho ...

  5. 读薄《深入理解 Java 虚拟机》 JVM 的内存分配策略

    内存分配规则不是固定的,取决于当前使用的是哪一种垃圾收集器以及虚拟机配置. 对象优先在 Eden 上分配 大多数情况下,对象分配在 Eden 上,当内存不足的时候触发一次 Minor GC. 大对象分 ...

  6. 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

    最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...

  7. 《深入理解JAVA虚拟机》学习日志----一、自动内存管理机制(2.垃圾收集器与内存分配策略)

    二.垃圾收集器与内存分配策略 前言:讨论的区域集中在Java堆和方法区中,而其他几个区域的内存分配和回收都具备确定性,所以不需过多考虑回收 的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了. ...

  8. java内存分配和垃圾回收_深入理解java虚拟机(二)垃圾收集器与内存分配策略...

    垃圾收集器与内存分配策略 垃圾收集,三个步骤 什么时候收集,收集那些,怎么收集 1.收集那些 我们会将一些不使用的对象进行收集,进行回收内存空间,我们怎么知道呢 1.引用计数法 如果这个实例被其他地方 ...

  9. java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略

    java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略 GC需要完成的三件事情:哪些内存需要回收.什么时候回收.如何回收 垃圾回收器在对堆进行回收前,首先要确定那些对象存活,哪些对象已经死去,判断的 ...

  10. java eden分配参数,JVM垃圾收集器与内存分配策略,

    垃圾收集器与内存分配策略 对象存活判断 引用计数算法 给对象添加一个计数器,每有一个引用+1,当引用失效-1,若为0则不在被使用. 可达性分析算法 对象是否可到达GC roots 或者说GC root ...

最新文章

  1. 170个新项目,579个活跃代码仓库,Facebook开源年度回顾
  2. 在ASP.NET AJAX中使用应用程序服务和本地化(3):用户个性化组件ProfileService
  3. Golang中函数不支持重载
  4. 2种IO并发开发中的设计模式:Reactor and Proactor
  5. Java序列化报错serialVersionUID不一致
  6. JDBC连接池和DBUtils
  7. 7-181 帅到没朋友 (20 分)
  8. java.Net.UnknownHostException异常
  9. 数组的存储与初始化、对象数组、数组作为函数参数
  10. 标记区域 Region
  11. 傅里叶变换和拉普拉斯变换公式总结
  12. 风口来了?关于电子信息工程专业的有关介绍
  13. HTTP协议报文头部结构和Web相关工具
  14. LaTeX之非英语字母输入
  15. 关于GIS中Scale和Resolution的那些事儿
  16. YY语音CTO赵斌的技术创业
  17. 2.K8S部署-------- 制作CA证书
  18. 芯片k66中文数据手册_IXFK66N50Q2
  19. Mysql中WhereIn和Join的性能比对
  20. OpenCV入门(七)快速学会OpenCV6色彩空间

热门文章

  1. 好烦,一封报警邮件,大量服务节点 redis 响应超时,又得要捉“虫”!
  2. 2018年100个让程序员崩溃的瞬间!看到哪一个你哭了?
  3. 专科学历 工作机会少的可怜?这个简单方法帮几千人提升3倍工作机会
  4. 讨薪后续:我和欠薪老板在派出所调解成功
  5. 推荐一些不错的公众号【二】
  6. 边工作边刷题:70天一遍leetcode: day 45-1
  7. webWMS开发过程记录(三)- 需求分析(略)
  8. 阅读《构建执法》11-12章
  9. VC下关于debug和release的不同的讨论(收藏-转载)
  10. 区块链供应链金融应用场景解决方案