java虚拟机垃圾回收机制

     2022-03-21     293

关键词:

在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学习交流QQ群:523047986  禁止闲聊,非喜勿进!

java虚拟机垃圾回收机制和java排错三剑客

一、Java虚拟机逻辑回收机制1、Java垃圾回收器        Java垃圾回收器是Java虚拟机(JVM)的三个重要模块(另外两个是解释器和多线程机制)之一,为应用程序提供内存的自动分配(MemoryAllocation)、自动回收(Ga... 查看详情

深入理解java虚拟机-垃圾回收机制(gc)

...ava堆和方法区是垃圾回收的主要内存区域,程序计数器、虚拟机栈、本地方法栈这几个内存区域是现成私有的,线程结束时内存自然也就回收了。如何判断对象可回收?在java堆里存放着几乎所有的对象实例,垃圾收集器在进行... 查看详情

java虚拟机原理垃圾回收算法(java虚拟机内存分区|垃圾回收机制|引用计数器算法|引用计数循环引用弊端)

文章目录一、Java虚拟机内存分区二、垃圾回收机制三、引用计数器算法(无法解决循环引用问题)一、Java虚拟机内存分区Java虚拟机内存分区:所有线程共有的内存区域:堆,方法区(元空间);方法区:存放静态变量,常量,class编译后的代... 查看详情

java虚拟机——垃圾收集机制

垃圾回收介绍??Java虚拟机内存划分讲到了Java内存运行时区域的各个部分,其中程序计数器,虚拟机栈,本地方法栈三个区域随线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出有条不紊地执行着出栈和入栈操作。每一... 查看详情

老生常谈java虚拟机垃圾回收机制(必看篇)(代码片段)

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

java内存分配及垃圾回收机制初探

一、运行时内存分配Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的... 查看详情

java垃圾回收机制

...http://blog.csdn.net/lom9357bye/article/details/488959871、数据划分(1)虚拟机栈:用来存放一些局部变量、方法出口等,生命周期随着程序的结束而结束(2)堆:对于引用类型的实例和数组都在堆上分配,java垃圾回收机制就是对堆中额度内存... 查看详情

jvm参数调优与垃圾回收机制(代码片段)

自动内存管理机制Java虚拟机原理 所谓虚拟机,就是一台虚拟的机器。他是一款软件,用来执行一系列虚拟计算指令,大体上虚拟机可以分为系统虚拟机和程序虚拟机,大名鼎鼎的VisualBox、Vmare就属于系统虚拟机,他们完全是... 查看详情

java垃圾回收机制是啥?

...用遍历。参考技术Ajava的垃圾回收机制:1.垃圾回收是由虚拟机自动执行,不能人为地干预。2.系统比较空闲(垃圾回收线程)3.对象不在被引用.对象处于引用的隔离岛状态(隔离引用),对象具备了回收的条件4.gc()方法,可以建... 查看详情

jvm虚拟机:jvm垃圾回收机制概念及其算法(代码片段)

垃圾回收概念和其算法谈到垃圾回收(GarbageCollection)GC,需要先澄清什么是垃圾,类比日常生活中的垃圾,我们会把他们丢入垃圾箱,然后倒掉。GC中的垃圾,特指存于内存中、不会再被使用的对象,儿回收就是相当于把垃圾“... 查看详情

java两种核心机制

Java虚拟机(JavaVirtalMachine) JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器。对于不同的平台,有不同的虚拟机。Java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次... 查看详情

深入理解java虚拟机-java内存区域,垃圾回收机制和内存分配策略(代码片段)

本篇主要参考周志明老师的《深入理解Java虚拟机》第三版一个Java程序,首先要经过javac编译成.class文件,.class文件是给JVM进行识别的,JVM将.class文件加载到方法区,执行引擎会执行这些字节码,执行时,... 查看详情

jvm及垃圾回收机制原理

JVMJava虚拟机Java虚拟机(Javavirtualmachine,JVM)是运行Java程序必不可少的机制。JVM实现了Java语言最重要的特征:即平台无关性。原理:编译后的Java程序指令并不直接在硬件系统的CPU上执行,而是由JVM执行。JVM屏蔽了与具体平台相... 查看详情

java垃圾回收机制

...外的人想进去,墙外的人想出来。简单可以理解为:JAVA虚拟机(JVM)将程序员和内存分离,所以相应的在JVM中,程序员不能直接操作内存也就成为它的劣势了。那么垃圾收集机制(GCGabageCollection)具体的工作过程是怎么样的呢... 查看详情

java垃圾回收机制(转)

...用什么样的策略来进行回收(释放空间)?在目前的商业虚拟机 查看详情

2.jvm垃圾回收机制

...要回收?  jvm的内存结构包含五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。其中,程序计数器、虚拟机栈、方法栈3个区域随线程而生,随线程而灭,因此这届个区域的内存和回收都具备确定性,就不需要... 查看详情

java垃圾回收机制(未掌握)

...回收效率.将对象分为三种状态:年轻代,年老代,持久代.JVM虚拟机将堆划分为Eden,Survivor和Tenured/Old空间年轻 查看详情

jvm内存回收机制

...问题,也不需要像C程序员那样战战兢兢。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。概括地说,该机制对JVM(JavaVirtualMachine)中的 查看详情