JVM 垃圾回收机制

首先JVM的内存结构包括五大区域: 程序计数器、虚拟机栈、本地方法栈、方法区、堆区。其中程序计数器、虚拟机栈和本地方法栈3个区域随线程启动与销毁, 因此这几个区域的内存分配和回收都具有确定性,不需要过多考虑回收的问题。而Java堆区和方法区则不一样,这部分内存的分配和回收是动态的,正式垃圾回收需要关注的部分。

垃圾回收在堆内存进行回收前, 要先确定区域的哪些对象是可以被回收的、那些对象暂时还不能回收,下面谈一谈判断对象是否存活的算法。

判断对象是否存活的算法

1.引用计数算法

引用计数算法:堆中的每个对象实例都有一个引用计数器,当一个对象被创建时,就将该对象实例分配给一个变量,该引用计数器设置为1,当任何其他变量被赋值为这个对象的引用时,计数加1,当一个对象实例的某个引用超过了生命周期或被赋为一个新值时, 引用计数减1。

任何引用计数器为0的对象实例都可以进行垃圾回收。当一个对象实例被垃圾回收时,它引用的所有对象实例引用计数器减1.

优点:引用计数器可以很快的执行,对程序不需要长时间的打断

缺点:无法检测出循环引用。如对象A有对象B的引用,对象B又有对象A的引用,这样他们的引用计数永远都不为0

2.可达性分析算法

可达性算法:将所有的引用关系看作一张图,从一个节点GC Root开始,寻找对应的引用节点,找到后继续寻找这个节点的引用节点,当所有引用节点寻找完毕后,剩余的节点就被认为是没有被引用的节点,即无用节点,无用节点被判定为可回收对象。

Java中可以作为GC Root的包括下面几种:

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

对于Java中的引用类型可以看这篇文章Java 控制类的引用类型,合理使用内存

常用的垃圾回收算法

1.标记-清除算法

标记-清除算法采用从根集合(GC Roots)进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行垃圾回收

这种算法实现起来比较容易,但是会造成内存碎片

2.标记-复制算法

复制算法是为了解决标记-清除算法的缺陷而提出的。

它将内存划分为大小相等的两块,每次只使用其中的一块。当这A快内存用完了,就将还存活的对象复制到B块上面,然后把A块的内存空间一次性清理掉

这种算法虽然实现简单,运行高效且不易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能使用的空间缩减为原来的一半。很显然,复制算法的效率跟存活对象的数量有很大关联,若存活对象很多,那么效率将大大降低

3.标记-整理算法

该算法是为了解决复制算法的缺陷,充分利用内存空间而提出的。

该算法与标记-清除算法一样,但是在完成标记后,不直接清理可回收对象,而是将存活对象全部向一端移动,接着清理掉边界以外的内存。

4.分代收集算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。其核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。

将其分为年轻代、老年代和永久代。然后根据不同的区域采用合适的收集算法。

Java一般将堆区分为年轻代和老年代,将方法区划为永久代。

下面对不同的年龄代进行简单说明

年轻代:新创建的对象都存放在这里。因为年轻代会频繁的进行GC清理,JVM在年轻代采用的是标记-复制算法,先标记出存活的实例,然后清除掉无用实例,将存活的实例根据年龄(每个实例被经历一次GC后年龄会加1)拷贝到不同的年龄代。

老年代:老年代中是经历了N此垃圾祸首后仍然存活的对象,其中的N由JVM的参数决定。这块内存区域一般大于年轻代。GC发生的次数也比年轻代要少。

永久代:用于存放静态文件,如Java类、方法等。为方法区。

方法区主要回收的内容有:废弃的常量、无用的类,对与废弃常量可以同过引用的可达性判断,但是对于无用类需要同时满足以下3个条件:

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

GC在什么时候触发

GC在优先级最低的线程中运行,一般在应用程序空闲时被调用。当内存不足时才会主动调用

因为对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有如下两种:

1.Scavenge GC

一般情况下,当新对象生成,并且在年轻代申请空间失败时,会触发Scavenge GC, 对年轻代进行垃圾回收。这种方式的GC不会影响到老年代。因为大部分对象都是年轻代开始的,同时年轻代内存不会分配的很大,所有年轻代的GC会频繁的进行。所以在这里要使用速度快、效率高的算法,使其空间尽快空出来。

若GC一次后仍不能满足内存分配,JVM会进行二次GC,若仍无法满足,则报“out of memory"的错误,Java应用将停止

2.Full GC

对整个内存进行整理,包括年轻代、老年代和永久代,所以Full GC比Scavenge GC要慢, 因此应该尽量减少Full GC的次数。以下可能引发Full GC的原因:

  1. 老年代被写满
  2. 永久代被写满
  3. System.gc()被显示调用
  4. 上一次GC后堆的各域分配策略动态变化。


Java的垃圾回收介绍到这,下面在说说如何在程序中减少GC的开销的几个建议:

  1. 不要显式调用System.gc()。此函数建议JVM进行GC,虽然只是建议,但是大多数情况下会触发GC,增加了间歇性停顿的次数,大大影响系统的性能
  2. 尽量减少临时对象的使用。也就是减少Scavenge GC执行的机会
  3. 对象不用时最好显式置为null。将不用的对象置为null,有利于GC收集器判定,从而提高GC的效率
  4. 尽量减少静态对象变量。静态变量属于全局变量,不会被GC祸首。
  5. 能有基本类型的就不要用包装类。基本类型变量栈用的内存资源比对应的包装类要少的多
  6. 使用StringBuffer 而不是String类累加字符串。因为堆String类型进行加的时候,会创建新的String对象,而StringBuffer是可变长的,在原有基础上进行扩增,不会产生中间对象
  7. 分散对象创建或删除的时间。集中在短时间内大量创建新对象,特别是大对象,会突然需要大量内存,JVM在面临这种情况时只能进行GC,以回收内存或整合内存碎片,从而增加GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。

原文地址:https://www.cnblogs.com/hujingnb/p/10181620.html

时间: 2024-08-30 02:06:01

JVM 垃圾回收机制的相关文章

浅析JVM垃圾回收机制

首先我们需要知道Java的内存分配与回收全部由JVM垃圾回收机制自动完成.每种JVM实现可能采用不同的方法实现垃圾回收机制.在收购SUN之前,Oracle使用的是JRockit JVM,收购之后使用HotSpot JVM.目前Oracle拥有两种JVM实现并且一段时间后两个JVM实现会合二为一.HotSpot JVM是目前Oracle SE平台标准核心组件的一部分.市面上探讨垃圾回收机制,默认都是基于HotSpot JVM的.Ok,我们切入正题,先来看下JVM的内存区域模型, 这张图非常直观的展

JVM内存管理和JVM垃圾回收机制

JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采用不同的垃圾回收机制. AD: 你对JVM内存组成结构和JVM垃圾回收机制是否熟悉,这里和大家简单分享一下,希望对你的学习有所帮助,首先来看一下JVM内存结构,它是由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示. JVM学习笔记 JVM内存管理和JVM垃圾回收 JVM内存组成结构 JVM内存

java JVM垃圾回收机制

Java语言出来之前,大家都在拼命的写C或者C++的程序,而此时存在一个很大的矛盾,C++等语言创建对象要不断的去开辟空间,不用的时候有需要不断的去释放控件,既要写构造函数,又要写析构函数,很多时候都在重复的allocated,然后不停的~析构.于是,有人就提出,能不能写一段程序在实现这块功能,每次创建,释放控件的时候复用这段代码,而无需重复的书写呢? 1960年 基于MIT的Lisp首先提出了垃圾回收的概念,用于处理C语言等不停的析构操作,而这时Java还没有出世呢!所以实际上GC并不是Jav

JVM垃圾回收机制入门

前言 数据库是大家会普遍重视的一个领域,异步通信一般用不到,虚拟机在大部分时候不会出问题,常被人忽视,所以我打算先学习虚拟机,从零单排Java高性能问题. 堆内存存储结构 Java6是以年代来规划内存的,而Java7的G1收集器则相反,这里以Java6为准. Survivor1和Survivor2是一样大的,必有一个始终为空,容量小于Eden. 垃圾回收机制 年轻代采用复制算法,当回收时,将Eden和Survivor中还存活的对象一次性地复制到另外一块Survivor上,然后清理掉Eden和刚才

JVM垃圾回收机制(一)

垃圾回收器 垃圾回收器主要负责: 1. 分配内存: 2. 保证所有正在被引用的对象还存在在内存中: 3. 回收不再被程序所引用的对象所占内存: Hotspot VM提供的垃圾回收器是一个分代的垃圾回收器(Generational GC),它将内存划分为不同的阶段,即不同生命周期的对象被放置在不同的内存空间中.这样的设计是基于弱年代假设(Weak Generational Hypothesis):大多数对象会很快变得不可达,只有很少有老对象指向新对象的引用.这种方式可以减少垃圾回收的停顿时间以及大

JVM垃圾回收机制总结(3) :按代垃圾收集器

为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的 . 因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如Http请求中的Session对象.线程.Socket连接,这 类对象跟业务直接挂钩,因此生命周期比较长.但是还有一些对象,主要是程序运行过程中生成的临时变量,这些对象生命周期会比较短,比如:String对 象,由于其不变类的特性,系统会产生大量的这些对象,有些

JVM 垃圾回收机制( 一) 回收对象的判定

关于JVM 的垃圾回收机制,我们一般都没过多深入,因为JAVA 和 C++ 的一个很大区别就是,JAVA 帮我们做了垃圾回收,而不用像C++ 那么样手动进行回收,当然任何自动的东西都存在一定弊端,比如机器人,即使自动程度很高,但是在处理某些感情问题上,肯定处理上就会有遗漏,开个玩笑啦, 下面我们先来了解一下JVM 的垃圾回收是怎么回事. 一.如何判断对象已经死亡 JVM 会回收那些不在使用的对象,或者说是已经死亡的对象,从而达到节省空间的目的,那么我们肯定的判断哪些对象已经死了,不在使用呢? 1

JVM垃圾回收机制与算法

JVM内存由几个部分组成:堆.方法区.栈.程序计数器.本地方法栈 JVM垃圾回收仅针对公共内存区域,即:堆和方法区进行,因为只有这两个区域在运行时才能知道需要创建些对象,其内存分配和回收都是动态的. 一.垃圾回收策略 1.1分代管理 将堆和方法区按照对象不同年龄进行分代: (Young Generation and Old Generation)堆中会频繁创建对象,基于一种分代的思想,按照对象存活时间将堆划分为新生代和旧生代两部分,并不是一次垃圾回收新生代存活的对象就放入旧生代, 而是要经过几次

简单测试JVM垃圾回收机制 System.gc()方法

简单的写一个方法测试Java的垃圾回收机制 System.gc()可以提醒JVM虚拟机去进行垃圾回收了,但是不一定成功. /** * 覆写Object中的finalize()方法 */ public class LJ { @Override protected void finalize() throws Throwable { System.out.println("垃圾正在回收..........."); } } public class GCDemo { /** * @param

深入JVM垃圾回收机制,值得你收藏

JVM可以说是为了Java开发人员屏蔽了很多复杂性,让Java开发的变的更加简单,让开发人员更加关注业务而不必关心底层技术细节,这些复杂性包括内存管理,垃圾回收,跨平台等,今天我们主要看看JVM的垃圾回收机制是怎么运行的,希望能够帮到大家, 哪些对象是垃圾呢? Java程序运行过程中时刻都在产生很多对象,我们都知道这些对象实例是被存储在堆内存中,JVM的垃圾回收也主要是针对这部分内存,每个对象都有自己的生命周期,在这个对象不被使用时,这个对象将会变成垃圾对象被回收,内存被释放,那么如何判断这个对