Java 垃圾收集技术
前言
在计算机科学中,垃圾回收(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 Web技术内幕(修订版)
阿里巴巴集团技术丛书 深入分析Java Web技术内幕(修订版)(阿里巴巴集团技术丛书.技术大牛范禹.玉伯.毕玄联合力荐!大型互联网公司开发应用实践!) 许令波 著 ISBN 978-7-121- ...
- Java垃圾收集器
概述 说起垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物.事实上,GC的历史远远比Java久远,1960年诞生于MIT的Lisp是第一门真正使用 ...
- 《深入分析Java Web技术内幕》读书笔记 - 第1章 深入Web请求过程
第1章 深入Web请求过程 1 1.1 B/S网络架构概述 2 基于统一的应用层协议HTTP来交互数据. 1.2 如何发起一个请求 4 HTTP连接本质是建立Socket连接.请求实现方式:工具包如H ...
- 面试官,不要再问我“Java 垃圾收集器”了
如果Java虚拟机中标记清除算法.标记整理算法.复制算法.分代算法这些属于GC收集算法中的方法论,那么"GC收集器"则是这些方法论的具体实现. 在面试过程中这个深度的问题涉及的比较 ...
- 面试官,不要再问我“Java 垃圾收集器”了(转载)
如果Java虚拟机中标记清除算法.标记整理算法.复制算法.分代算法这些属于GC收集算法中的方法论,那么"GC收集器"则是这些方法论的具体实现. 在 面试过程中这个深度的问题涉及的比 ...
- Java基础技术JVM面试【笔记】
Java基础技术JVM面试[笔记] JVM JVM 对 java 类的使用总体上可以分为两部分:一是把静态的 class 文件加载到 JVM 内存,二是在 JVM 内存中进行 Java 类的生命周期管 ...
- JVM基础学习(二):内存分配策略与垃圾收集技术
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来 垃圾收集概述 Java内存模型中的堆和方法区是垃圾收集技术所需要关注的终点,因为其他的区域会跟 ...
- Java数据库连接技术——JDBC
大家好,今天我们学习了Java如何连接数据库.之前学过.net语言的数据库操作,感觉就是一通百通,大同小异. JDBC是Java数据库连接技术的简称,提供连接各种常用数据库的能力. JDBC API ...
- java 深入技术八(内省)
1. javabean的软件设计思想 2.内省:封装了java反射,提供直接操作属性的Setter和getter方法的方法 3.核心API:BeanInfo java 的描述信息,Introspect ...
随机推荐
- <JZOJ5941>乘
emmm还挺妙 不过我没想到qwq 考场上瞎写的还mle了心碎 把b分两..预处理下 O1询问qwq #include<cstdio> #include<iostream> # ...
- 如何为MyEclipse添加XML文档所使用的DTD
1.打开MyEclipse,找到菜单栏"Window"---->"Preferences(首选项)": 2.在左侧导航菜单栏找到"MyEclip ...
- python 常用模块介绍
1.定义 模块:用来从逻辑上组织python代码(变量.函数.类,逻辑),本质就是.py结尾的python文件(文件名:test.py,对应的模块名:test). 包:用来从逻辑上组织模块的,本质就是 ...
- 查漏补缺:C++STL简述(容器部分)
STL:是Standard Template Library的简称,中文译为标准模板库,是由惠普实验室开发的一系列软件的统称,现为C++的一部分,可分为容器(containers).迭代器(itera ...
- zabbix监控mysql数据库信息脚本
---恢复内容开始--- 在/usr/local/zabbix/etc/zabbix_agentd.conf增加 # 获取mysql性能指标 UserParameter=mysql.status[*] ...
- IDEA 运行junit单元测试方法
配置Run,增加Junit 最终配置如下:
- C++扬帆远航——16(猜数字)
/* * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:guessnum.cpp * 作者:常轩 * 微信公众号:Wor ...
- TypeScript声明文件
为什么需要声明? 声明的本质是告知编译器一个标识符的类型信息.同时,在使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全.接口提示等功能. 声明在TypeScript中至关重要,只有通过 ...
- Python基本小程序
目录 Python基本小程序 一.筛选从1-100所有的奇数 二.筛选从0-100所有的偶数 三.求1-100之间所有的偶数和,奇数和 四.三个数由小到大输出 五.四个数字重复数字的三位数 Pytho ...
- win10下LoadRunner12 下载安装图文教程
1.下载安装包: 链接:https://pan.baidu.com/s/1hiGC9FjfKOFRWHVfMAHaeg 提取码:sr8x 如下图所示,咱们直接安装社区版“HP_LoadRunner_1 ...