前言

在计算机科学中,垃圾回收(GC: garbage collection)是内存自动管理的一种方式,它并不是同 Java 语言一起诞生的,实际上,早在 1959 年为了简化 Lisp 语言的手动内存管理,该语言的作者就开始使用了内存自动管理技术。 垃圾收集和手动内存管理刚好相反,后者需要编程人员自己去指定需要释放的对象然后将内存归还给操作系统,而前者不需要关心给对象分配的内存回收问题。Java 语言使用自动垃圾收集器来管理对象生命周期中的内存,要进行垃圾收集首先需要明确三个问题:1. 哪些内存需要回收、2. 什么时候进行回收、3. 怎么进行内存回收。接下来让我们一起看看 Java 语言对这些问题是如何处理的。

哪些内存需要回收

为了方便管理和跨平台,Java 虚拟机规范规定在执行 Java 程序的时候把它所管理的内存划分为若干个不同的数据区域。这些区域都有着各自不同的用途以及创建和销毁的时间,有的数据区域随着用户线程的启动和结束而建立和销毁,有的区域会随着虚拟机进程的启动和停止而存在和销毁。更多有关运行时数据区域的内容请看 Java 运行时数据区域。

由于 Java 运行时数据区域中的 程序计数器、虚拟机栈和本地方法栈和线程的生命周期一致,随线程的启动和结束而建立和销毁。而且当我们的类结构确定了之后,在编译期间,一个栈帧需要分配内存的大小基本上也就确定下来了,这三个区域的内存分配和收回都是具备确定性的,不需要我们过多的去考虑内存回收问题。主要考虑Java 堆和方法区的内存回收的问题。

什么时候进行回收

在 Java 语言中,一个对象的生命周期分为以下三个阶段:

对象创建阶段 通常我们使用 new 关键字进行对象创建 e.g. Object obj = new Object();,当我们创建对象时,Java 虚拟机将分配一定大小的内存来存储该对象,分配的内存量可能会根据虚拟机厂商的不同而有所不同。

对象使用阶段 在这个阶段,对象被应用程序的其它对象使用(其它活动对象拥有指向它的引用)。在使用期间,该对象会一直驻留在内存当中,并且可能包含对其它对象的引用。

对象销毁阶段 垃圾收集系统监视对象,如果发现对象不被任何对象引用了,则进行该对象内存回收操作。

那么问题来了,该如何去判断一个对象有没有被引用呢?目前,主要有两种判断对象是否存活的算法,分别是 引用计数算法(Reference counting algorithm)和可达性分析算法(Accessibility analysis algorithm)。

引用计数算法

首先我们看看引用计数算法是如何判断的,该算法的主要思想就是给每个对象都添加一个引用计数器,当该对象被变量或者另一个对象引用时该计数器值就会加 1,同时当对象的一个引用无效时,对象计数器的值会相应的减 1。当对象引用计数器的值为 0 时,说明该对象已经不再被引用了,那么就可以销毁对象进行内存回收操作了。这个算法的实现比较简单,对象是否“存活”的判断效率也比较高,这个算法看起来确实不错,但是它有个致命的缺点就是:无法解决对象间相互引用的问题。相互引用简单来说就是,有两个对象 object1 和 object2 都有一个引用类型字段 ref,并且做了如下赋值操作:

object1.ref = object2;

object2.ref = object1;

这两个对象除了上面这个赋值之外,不被其它任何对象引用,实际上这两个对象都不可能再被访问了,但是因为它们俩都互相引用了对方,导致引用计数器不为 0,导致使用引用计数器算法的 垃圾收集器 无法收集它们,它们就会一直存在于内存之中直到虚拟机进程结束。正是因为这个原因,市场上主流的 Java 虚拟机大部分都没有选用这个算法来管理内存,下面介绍的 可达性分析算法 就可以很好的避免了对象间相互引用的问题。

可达性分析算法

Java 虚拟机是通过可达性分析算法来判断对象是否存活的,该算法的主要思想是将一系列称为 GC Root 的对象作为起点,向下进行搜索,搜索经过的路径称为引用链(Reference chain),当一个对象到 GC Root 对象没有任何引用链的时候,则表示该对象是不可达的,可以对其进行内存回收。

在 Java 虚拟机中,规定以下几种情况可以作为 GC Root 对象:

虚拟机栈中引用的对象

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

方法区中常量引用的对象

本地方法栈中 Native 方法引用的对象

怎么进行内存回收

当我们创建的对象不可达之后,Java 虚拟机会在后台自动去收集回收不可达对象的内存,自 Java 语言诞生以来,在垃圾收集算法上进行了许多更新,主要有标记-清除算法(Mark and sweep algorithm)、复制算法(Copying algorithm)、标记—整理算法(Mark and compact algorithm)和分代收集算法(Generational collection algorithm),根据这些算法实现的垃圾收集器在后台默默运行以释放内存,下面让我们看看它们是如何工作的。

标记-清除算法(mark and sweep algorithm)

标记—清除算法是初始且非常基本的算法,主要分为以下两个阶段:

标记需要回收对象,找出程序中所有需要回收的对象并标记。

清除所有标记对象,在标记完成后统一回收被标记对象。

首先标记出需要回收的对象,标记完成后再统一回收被标记对象。这个算法是最基础的垃圾收集算法,后面将要介绍的几个算法都是在它的基础上优化改进的,算法主要有两个不足的地方:① 效率不高,标记和清除过程的效率都不高。② 空间利用率不高,标记清除之后会产生大量不连续的内存碎片,后面如果要分配大对象的时候由于连续内存不足可能会再次触发垃圾收集操作。

复制算法(copying algorithm)

复制算法就是为了解决标记—清除算法的效率问题的,主要思想就是将可用的内存分为大小相等的两个部分,每一次都只使用其中的一块,当这块内存使用完了之后,就将依然存活的对象复制到另一块内存上去,然后再把这块含有可回收对象的内存清理掉,这样每次都是清理一半的连续内存了,就不会存在内存碎片的情况。但是这个算法的缺点也很明显,它把可用内存的大小缩小到了一半。

标记-整理算法(mark and compact algorithm)

如果对象的存活率比较低的情况下,上面介绍的复制算法效率还是很高的,毕竟只要复制少部分存活对象到另一块内存中即可,但是当对象的存活率比较高时就会进行多次复制操作。比如老年代,老年代的对象是经过多次垃圾回收依然存活的对象,对象的存活率相对来说比较高,根据老年代的这个特点,于是针对这种情况就有了另一个算法称之为标记-整理算法,主要思想和其名字一样也是分为标记和整理两个阶段,第一个标记阶段依然和标记—清除算法一样,后面的第二个整理阶段就不是直接对可回收对象进行清理了,而是让所有存活的对象都向内存的同一侧移动,然后就直接清除掉另一侧的内存。

分代收集算法(generational collection algorithm)

根据不同分代的特点,现在商业上的虚拟机针对不同的分代采取适合的垃圾收集,一般是把 Java 堆分为新生代和老年代。在新生代中,对象大部分存活时间都很短每次垃圾收集都会有很多的对象被清除,只有少部分对象可以存活下来,那么此时就可以使用复制算法,只需要复制出少部分存活的对象即可效率高。然而在老年代中大部分对象的存活时间比较长,则需采用标记-清除算法或者标记-整理算法来进行垃圾收集。

垃圾收集算法对于垃圾回收来说类似于我们程序中的接口,是一套垃圾回收的指导算法,算法的具体实现我们称之为垃圾收集器。但是 Java 虚拟机规范中并没有对垃圾收集器的实现有任何规定。所以不同的厂商和不同版本的虚拟机实现的垃圾收集器也不一样,不过一般都会提供一些配置参数来让用户根据自身情况来设置所需的垃圾收集器。

JVM 相关 GC 配置

Java 虚拟机部分垃圾收集(Garbage Collection,GC)相关配置如下

参数

描述

-Xms2048m

设置初始堆大小(新生代 + 老年代)

-XX:InitialHeapSize=3g

设置初始堆大小(新生代 + 老年代)

-Xmx3g

设置最大堆大小(新生代 + 老年代)

-XX:MaxHeapSize=3g

设置最大堆大小(新生代 + 老年代)

-XX:NewSize=128m

设置堆初始新生代大小

-XX:MaxNewSize=128m

设置堆最大新生代大小

-XX:PermSize=512m(JDK 1.7)

设置初始永久代(元空间)大小

-XX:MetaspaceSize=512m(JDK 1.8+)

设置初始永久代(元空间)大小

-XX:MaxPermSize=1g(JDK 1.7)

设置最大永久代(元空间)大小

-XX:MaxMetaspaceSize=1g(JDK 1.8+)

设置最大永久代(元空间)大小

-XX:+DisableExplicitGC

忽略应用程序对 System.gc() 方法的任何调用

-XX:+PrintGCDetails

打印输出 GC 收集相关信息

参考文章

垃圾收集 java_Java 垃圾收集技术相关推荐

  1. 垃圾收集算法,垃圾收集器_垃圾收集器准则和提示

    垃圾收集算法,垃圾收集器 这些是我需要调整GC时通常会看到的一些准则和技巧. 主要由以下两本书组成,而根据我的经验却很少: Java性能 JBoss AS 5性能调优 希望它们对在那里的其他人有用! ...

  2. 垃圾收集算法,垃圾收集器_弱,弱,最弱,利用专家参考来管理垃圾收集器

    垃圾收集算法,垃圾收集器 何时以及何时不使用Java中的专家引用 弱引用,软引用和幻像引用既危险又强大. 如果以错误的方式使用它们,则会破坏JVM性能. 但是,如果使用正确的方法,它们可以大大提高性能 ...

  3. 细说JVM系列:自动内存管理内存回收:垃圾收集理论-垃圾收集算法

    垃圾收集理论-垃圾收集算法 这里主要讲解垃圾收集理论上的算法,下一篇会介绍一些实现了这些算法的垃圾收集器. 一般我们谈垃圾收集从三个问题来帮你理解jvm的垃圾收集策略: 1.怎么判断哪些内存是垃圾? ...

  4. 垃圾收集算法,垃圾收集器_确定活动的热点垃圾收集器

    垃圾收集算法,垃圾收集器 StackOverflow问题查找正在运行哪种类型的垃圾收集 器,jvm的默认垃圾收集器 , 如何通过查看gc日志来查看正在运行的垃圾收集器? ,以及如何知道HotSpot ...

  5. 垃圾收集算法,垃圾收集器_您正在使用什么垃圾收集器?

    垃圾收集算法,垃圾收集器 我们的研究实验室正全速前进. 随着最近的资本注入 ,我们只能保证我们不断创新的步伐只会加快. 我们进行的部分研究与GC优化有关. 在处理这个有趣的领域中的问题时,我们认为可以 ...

  6. 垃圾收集算法,垃圾收集器_为什么我不能关闭垃圾收集器?

    垃圾收集算法,垃圾收集器 让我们开始快速回顾一下我作为Java开发人员的职业生涯的早期. 我想消除正在进行的测试中的垃圾回收(GC)暂停. 瞧,当我发现无法完成时,我很生气. 那时,我把问题留在了&q ...

  7. 垃圾收集 java_Java的内置垃圾收集如何使您的生活更美好(大部分时间)

    垃圾收集 java 通过从您的应用程序学习企业APM产品,发现更快,更有效的性能监控. 参加AppDynamics APM导览! "无需为用户编写将寄存器返回到自由存储列表的程序." ...

  8. 垃圾收集算法,垃圾收集器_实时垃圾收集

    实时系统和垃圾收集 实时(RT)应用程序开发通过对部分运行时行为施加时间限制,使其与通用应用程序开发区分开来. 此类限制通常放在应用程序的各个部分(例如中断处理程序)上,其中响应中断的代码必须在给定时 ...

  9. 四、垃圾收集之垃圾收集算法

    2019独角兽企业重金招聘Python工程师标准>>> 一.标记-清除算法 最基础的收集算法,其余的算法基本都是由此算法改进得来 效率不高,标记和清除的过程效率都不高 清除之后会产生 ...

  10. Java-虚拟机-垃圾收集器/垃圾收集算法/GCROOT根

    本文应该与堆的内存规划合二为一,不过还不知道如何排版,所以目前就先这样子吧 概念:STW,stop the word,指的是当前我们自己的应用线程暂停,但是虚拟机的GC线程依然运行 垃圾回收算法 1. ...

最新文章

  1. CBAC ftp测试
  2. BZOJ3574 HNOI2014抄卡组(哈希)
  3. 当我说要做大数据工程师时他们都笑我,直到三个月后……转
  4. 【干货】路由黑洞的5种解决方法大PK
  5. VTK:图片之DrawShapes
  6. 前端真的能做到彻底权限控制吗?
  7. 腾讯开源再获OSCAR 5项大奖,全国首家可信开源治理认证自发开源企业
  8. python 类初始化参数校验_如何规避python参数的初始化次数?
  9. 7.4 流水线的冒险
  10. Dart的日期时间操作
  11. 同步图计算:GraphLite的安装和使用
  12. 【Python - wxpython】- 卫星通信系统链路计算软件
  13. mysql怎么设置001,[MySQL光速入门]001 让MySQL跑起来
  14. 推荐 :数据科学研究的现状与趋势
  15. html5微课程制作,翟猛老师《微课开发及制作-基于H5课件制作模式》
  16. 如何通过mac微信版把文件传到手机上?
  17. go-cms golang内容管理系统, vue分离+自动代码生成
  18. pytorch_lesson13.2 模型拟合度概念介绍+模型欠拟合实例+单隐藏层激活函数性能比较+相同激活函数不同隐藏层数结果对比+神经网络结构选择策略
  19. 瑞利分布理论和近似概率密度函数
  20. 视频学习笔记------系统学习让你轻松定义 Java 类加载器

热门文章

  1. 一个GUI程序究竟会有几个线程?
  2. Java HashSet和ArrayList的查找Contains()时间复杂度
  3. 使用注解方式搭建SpringMVC
  4. 《学习之道》第六章补充
  5. Win7+Ubuntu双系统时间不一致
  6. sqlsever 创建一个通用分页查询
  7. NLog.config
  8. GDI绘制矩形缺少右边和底部边界线问题
  9. 2017年1月14 15开车总结 英西
  10. php调用一个c语言写的接口问题