JVM垃圾回收机制概述

总览

本文会介绍垃圾回收的以下几个方面。

  • 为什么要垃圾回收
  • 在哪里回收
  • 哪些对象需要回收
  • 怎么回收
  • HotSpotJVM中有哪些具体的回收器可以直接用。

在开始讲垃圾回收之前,先通过一张图快速回忆一下运行时内存

为什么需要垃圾回收

  • 因为程序在运行的过程中,对象实例,变量会占据越来越多的内存,如果不及时的清理,会造成性能下降和内存耗尽的问题。

从哪里回收

  • 堆和方法区。堆里面不再使用的对象实例,方法区里面的不再使用的常量和类。

如何判断一个对象需要回收呢?

  • 有两种算法。

    • 引用计数法,就是对象被引用一次就+1, 当引用为0的时候就进行回收。

      这种算法缺点是当两个对象互相引用的时候,就不会被回收。

  • 可达性分析法:通过对象是否能够通过GC ROOT的引用链可达 判断 对象是否可以被回收。

     

   上图就是一个可达性分析的常见场景,对象三什么时候回被回收?先上结果:对象三是软可达,正常情况下在内存不足的时候会被回收。

   这里需要理解两个概念

    • 一个是哪些数据可以作为GC ROOT,虚拟机栈中本地变量表引用的对象

      • 方法区中

        • 类静态变量引用的对象
        • 常量引用的对象
      • 本地方法栈中JNI引用的对象
    • 一个是对象引用有哪几种类型。

       为了更加灵活的控制对象的生命周期,将对象引用分为四个级别。

      • 强引用( Strong Reference ):这是最常见的引用,不会被GC,除非你显示的置为null,才会GC。
Object o=new Object();
      • 软引用( Soft Reference ):用来描述有用但非必须的对象,内存不足的时候回GC,多用于caching/pooling中,比如下面例子网页缓存吃紧的时候回收内存。
SoftReference<String> sr = new SoftReference<String>(new String("hello"));
System.out.println(sr.get());
      • 弱引用( Weak Reference ):也是用来描述非必须的对象,在下一次GC的时候就会回收,跟内存是否不足无关。
Object obj = new Object();
WeakReference<Object> WeakRef =new WeakReference(obj);
Object objg = WeakRef.get();
      • 虚引用( Phantom Reference ) : 不会影响对象的生命周期,也无法通过虚引用获取对象实例,仅用于在发生GC的时候接受一个系统通知。

        • 必须和引用队列一起使用,主要用来判断对象是否将被垃圾回收器回收。
        • 垃圾回收器准备回收虚引用对象时,会将这个引用加入到与之关联的引用队列中,你通过引用队列判断到某个引用将要被回收就可以立即采取行动。
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
System.out.println(pr.get());

      理解了上面两个概念之后,我们在思考下面一个问题,如果一个对象的引用有多个,怎么通过可达性决定回收周期呢?

      有两个原则。

      • 单条引用链上,由最弱的一个引用类型决定
      • 多条引用链之间,由最强的一个引用类型决定

      上图中对象三是一个软可达对象,因为左半边的链最弱的是弱引用,右边的链最弱的是软引用,左右两边最强的是软引用。

知道了有对象需要回收之后,我们怎么回收呢?有三种方法

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

     先需要回收的对象做标记,之后扫描,将有标记的回收。这种算法效率不高,会产生碎片。最终导致大对象分配空间时无法找到足够大的内存而频繁触发垃圾回收。

  

  • 复制(Copying)算法

     为了解决标记-清除算法碎片的缺点,提出了复制算法,将内存容量按照大小分成两份,每次使用其中的一半,当内存耗尽之后将存活对象有序的复制到另一半中,并清理当前内存。

   这种算法也有缺点:浪费一般空间;效率也跟存活对象多少有关,存活对象多算法效率会将大大降低。

  

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

   为了解决复制算法浪费空间的缺点,并利用标记清理的优点,提出了这个算法,就是先标记回收对象,完了之后会将存活对象向同一端移动,然后清理需要回收的对象,这样就保证了空间的连续性。

    

  • 分代收集(Generational Collection)算法

     从回收对象生命周期不同的角度讲内存划分为不同的区域:新生代和老年代,并结合了前面的复制算法和标记整理算法。

  

    新生代中存放的是:新生成的对象实例,也是垃圾回收对象最多的区域,用到的是复制算法。

    老年代会存放的是:在新生代中经历过多次垃圾回收仍然存在的对象;另外新生代生成大对象空间不够时也会在老年代中分配。这里采用的是标记整理算法对老年代区域进行清理。

    其中,新生代又细分为三个区:Eden去,From Survivor区,To Survivor区,比例是8:1:1。

    每次会使用Eden和一块Survivor区(90%新生代内存)服务对象,在触发回收机制时会采用复制算法,将两块区中存活的对象复制到另外10%的内存中,然后清理待回收对象。

这里考虑一个有趣的问题,如果老年代的对象有引用到新生代的,这种情况怎么处理的呢?用的是写屏障,会将这种类型的所有引用都放到一个集合中,标记时需要处理这个集合。

HotSpot JVM基于上面的垃圾回收算法实现的垃圾收集器有哪几种呢?

  • Serial GC/Serial Old GC

    • 两者都属于单线程垃圾收集器,在垃圾收集时,必须暂停所有用户线程。
    • Serial用于新生代,采用复制算法,Serial Old用于老年代,采用标记-整理算法。
  • ParNew GC
    • 是Serial的多线程版本,使用多线程进行垃圾收集。
  • Parallel Scavenge GC/Parallel Old GC
    • 都是吞吐量优先的多线程垃圾收集器
    • Parallel Scavenge GC用于新生代,采用复制算法; Parallel Old GC 用于老年代,采用标记-整理算法。
  • CMS GC
    • Current Mark Sweep,一种以获取最小垃圾回收停顿时间为目标的收集器,并发,采用标记-清除算法。
  • G1 GC基于CMS有不少的改进。基于标记-整理算法,可以比较精确的控制停顿。
  • 具体使用哪种收集器,在JVM中可以通过下面的参数指定。

参考资料:

https://blog.csdn.net/zhangerqing/article/details/8214365

https://blog.csdn.net/wxfx888/article/details/78074145

原文地址:https://www.cnblogs.com/darrenqiao/p/9206554.html

时间: 2024-11-07 11:59:27

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 垃圾回收机制概述

摘要: Java技术体系中所提倡的 自动内存管理 最终可以归结为自动化地解决了两个问题:给对象分配内存 以及 回收分配给对象的内存,而且这两个问题针对的内存区域就是Java内存模型中的 堆区.关于对象分配内存问题,笔者的博文<JVM 内存模型概述>已经阐述了 如何划分可用空间及其涉及到的线程安全问题,本文将结合垃圾回收策略进一步给出内存分配规则.垃圾回收机制的引入可以有效的防止内存泄露.保证内存的有效使用,也大大解放了Java程序员的双手,使得他们在编写程序的时候不再需要考虑内存管理.本文着重

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)堆中会频繁创建对象,基于一种分代的思想,按照对象存活时间将堆划分为新生代和旧生代两部分,并不是一次垃圾回收新生代存活的对象就放入旧生代, 而是要经过几次