目录
垃圾对象的判定
引用计数法
可达性分析(根搜索法)
引用状态
垃圾回收算法
标记-清除算法
标记-整理算法
复制算法
分代收集算法
触发GC的类型
首页 Java java教程 Java中有关虚拟机垃圾回收机制的图文详解

Java中有关虚拟机垃圾回收机制的图文详解

Aug 09, 2017 am 09:31 AM
java 回收 垃圾

在Java虚拟机中,对象和数组的内存都是在堆中分配的,垃圾收集器主要回收的内存就是再堆内存中。如果在Java程序运行过程中,动态创建的对象或者数组没有及时得到回收,持续积累,最终堆内存就会被占满,导致OOM。

JVM提供了一种垃圾回收机制,简称GC机制。通过GC机制,能够在运行过程中将堆中的垃圾对象不断回收,从而保证程序的正常运行。

垃圾对象的判定

我们都知道,所谓“垃圾”对象,就是指我们在程序的运行过程中不再有用的对象,即不再存活的对象。那么怎么来判断堆中的对象是“垃圾”、不再存活的对象呢?

引用计数法

每个对象都有一个引用计数的属性,用来保存该对象被引用的次数。当引用次数为0时,就意味着该对象没有被引用了,也就不会在使用这个对象了,可以判定为垃圾对象。但是,这种方式有一个很大的Bug,就是无法解决对象间相互引用或者循环引用的问题:当两个对象相互引用,他们两个和其他任何对象也没有引用关系,它俩的引用次数都不为0,因此不会被回收,但实际上这两个对象已经不再有用了。

可达性分析(根搜索法)

为了避免使用引用计数法带来的问题,Java采用了可达性分析法来判断垃圾对象。

这种方式可以将所有对象的引用关系想象成一棵树,从树的根节点GC Root遍历所有引用的对象,树的节点就为可达对象,其他没有处于节点的对象则为不可达对象。

那么什么样的对象可以作为GC的根节点呢?

  • 虚拟机栈(帧栈中的本地变量表)中引用的对象

  • 方法区中静态属性引用的对象

  • 方法区中常量引用的对象

  • 本地方法栈中JNI引用的对象

引用状态

垃圾回收机制,不管采用是引用计数法,还是可达性分析法,都与对象的引用有关,Java中存在四种引用状态:

  • 强引用 - 我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,就表示它处于可达状态,垃圾回收器绝不会回收它,即便系统内存非常紧张,Java虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会回收被强引用所引用的对象。因此,强引用是造成Java内存泄露的主要原因之一。

  • 软引用 - 一个对象只具有软引用,如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。

  • 弱引用 - 一个对象只具有弱引用,那就类似于是可有可无的。弱引用和软引用很像,但弱引用的引用级别更低。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

  • 虚引用 - 一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收的活动,我们平常一般不会使用。

垃圾回收算法

通过可达性分析算法能够判定哪些对象是需要回收的了,那么回收具体需要怎样去执行呢?

标记-清除算法

首先需要标记可以回收的对象内存,然后在对回收的内存进行清除。

标记-清除算法(回收前)

标记-清除算法(回收后)

但是这样的话,随着程序的运行,会不断分配释放内存,在堆中会产生很多的不连续的空闲内存区,即内存碎片。这样即使有足够多的空闲内存,也不一定能分配出足够大的内存,并且可能会造成频繁的GC,影响效率,甚至OOM。

标记-整理算法

和标记-清除算法不同的是,标记-整理算法在标记后不直接清理可回收内存,而是将存活对象都移动到一端,然后清除掉可回收内存。

标记-整理算法(回收前)

标记-整理算法(回收后)

这样做的好处就是不会产生内存碎片。

复制算法

复制算法需要先将内存分为两块,先在其中一块内存上分配内存,当这块内存被分配完后,则执行垃圾回收,然后把存活对象全部复制到另一块内存上,第一块内存则全部清空。

复制算法(回收前)

复制算法(回收后)

这种算法不会产生内存碎片,但是相当于只能使用一半的内存空间。同时,复制算法和存活对象的数量有关,如果存活对象的数量多,那么复制算法的效率会大大降低。

分代收集算法

在Java虚拟机中,对象的生命周期有长有短,大部分对象的生命周期很短,只有少部分的对象才会在内存中存留较长时间,因此可以依据对象生命周期的长短将它们放在不同的区域。在采用分代收集算法的Java虚拟机堆中,一般分为三个区域,用来分别储存这三类对象:

  • 新生代 - 刚创建的对象,在代码运行时一般都会持续不断地创建新的对象,这些新创建的对象有很多是局部变量,很快就会变成垃圾对象。这些对象被放在一块称为新生代的内存区域。新生代的特点是垃圾对象多,存活对象少。

  • 老年代 - 一些对象很早被创建了,经历了多次GC也没有被回收,而是一直存活下来。这些对象被放在一块称为老年代的区域。老年代的特点是存活对象多,垃圾对象少。

  • 永久代 - 一些伴随虚拟机生命周期永久存在的对象,比如一些静态对象,常量等。这些对象被放在一块称为永久代的区域。永久代的特点是这些对象一般不需要垃圾回收,会在虚拟机运行过程中一直存活。(在Java1.7之前,方法区中存储的是永久代对象,Java1.7方法区的永久代对象移到了堆中,而在Java1.8永久代已经从堆中移除了,这块内存给了元空间。)

分代收集算法也就根据新生代和老年代来进行垃圾回收的。

对于新生代区域,每次GC都会有很多垃圾对象被回收,只有少量存活。因此采用复制回收算法,GC时把剩余很少的存活对象复制过去即可。

在新生代区域中,并不是按照1:1的比例来进行复制回收,而是按照8:1:1的比例分为了Eden、SurvivorA、SurvivorB三个区域。其中Eden意为伊甸园,形容有很多新生对象在里面创建;Survivor区则为幸存者,即经历GC后仍然存活下来的对象。

  1. Eden区对外提供堆内存。当Eden区快要满了,则进行Minor GC(新生代GC),把存活对象放入SurvivorA区,清空Eden区;

  2. Eden区被清空后,继续对外提供堆内存;

  3. 当Eden区再次被填满,此时对Eden区和SurvivorA区同时进行Minor GC(新生代GC),把存活对象放入SurvivorB区,此时同时清空Eden区和SurvivorA区;

  4. Eden区继续对外提供堆内存,并重复上述过程,即在 Eden 区填满后,把Eden区和某个Survivor区的存活对象放到另一个Survivor区;

  5. 当某个Survivor区被填满,且仍有对象未被复制完毕时,或者某些对象在反复Survive 15次左右时,则把这部分剩余对象放到老年代区域;当老年区也被填满时,进行Major GC(老年代GC),对老年代区域进行垃圾回收。

老年代区域对象一般存活周期较长,每次GC时,存活的对象比较多,因此采用标记-整理算法,GC时移动少量存活对象,不会产生内存碎片。

触发GC的类型

Java虚拟机会把每次触发GC的信息打印出来,可以根据日志来分析触发GC的原因。

  • GC_FOR_MALLOC:表示是在堆上分配对象时内存不足触发的GC。

  • GC_CONCURRENT:当我们应用程序的堆内存达到一定量,或者可以理解为快要满的时候,系统会自动触发GC操作来释放内存。

  • GC_EXPLICIT:表示是应用程序调用System.gc、VMRuntime.gc接口或者收到SIGUSR1信号时触发的GC。

  • GC_BEFORE_OOM:表示是在准备抛OOM异常之前进行的最后努力而触发的GC。

以上是Java中有关虚拟机垃圾回收机制的图文详解的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Java 中的平方根 Java 中的平方根 Aug 30, 2024 pm 04:26 PM

Java 中的平方根指南。下面我们分别通过例子和代码实现来讨论平方根在Java中的工作原理。

Java 中的完美数 Java 中的完美数 Aug 30, 2024 pm 04:28 PM

Java 完美数指南。这里我们讨论定义,如何在 Java 中检查完美数?,示例和代码实现。

Java 中的随机数生成器 Java 中的随机数生成器 Aug 30, 2024 pm 04:27 PM

Java 随机数生成器指南。在这里,我们通过示例讨论 Java 中的函数,并通过示例讨论两个不同的生成器。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。这里我们通过示例讨论简介、如何使用weka java、平台类型和优点。

Java 中的史密斯数 Java 中的史密斯数 Aug 30, 2024 pm 04:28 PM

Java 史密斯数指南。这里我们讨论定义,如何在Java中检查史密斯号?带有代码实现的示例。

Java Spring 面试题 Java Spring 面试题 Aug 30, 2024 pm 04:29 PM

在本文中,我们保留了最常被问到的 Java Spring 面试问题及其详细答案。这样你就可以顺利通过面试。

突破或从Java 8流返回? 突破或从Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

Java 中的时间戳至今 Java 中的时间戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的时间戳到日期指南。这里我们还结合示例讨论了介绍以及如何在java中将时间戳转换为日期。

See all articles