Java垃圾回收手册翻译 - 什么是垃圾回收

初看之下,垃圾回收应该要做其名称之事 - 找到和丢掉垃圾。然而事实上它正好做着相反的事,垃圾回收会记录所有仍在使用中的对象,然后将其他标记为垃圾。谨记这点,我们开始挖掘更多Java虚拟机如何实现被称为垃圾回收的自动化内存回收过程的细节。

为了避免一头扎进细节,我们从头开始,解释垃圾回收的一般性质以及核心概念和方法。

免责声明:本手册关注于Oracle Hotspot和OpenJDK的表现,其他运行时环境甚至其他虚拟机,比如jRockit或IBM J9,会在某些方面有不同于本手册所涵盖的表现。

手动内存管理

在开始现代形式的垃圾回收内容之前,我们先快速回顾一下那些需要手动显示分配和释放数据内存的日子。那时候如果你忘记释放内存,你将不能重用那块内存。这部分内存会被声明到但是没有使用,这种场景叫做内存泄漏

下面是一个用C语言写的使用手动内存管理的简单例子:

int send_request() {
size_t n = read_size();
int *elements = malloc(n * sizeof(int)); if(read_elements(n, elements) < n) {
// elements not freed!
return -1;
} // … free(elements)
return 0;
}

正如我们所见,非常容易忘记释放内存。相比于现在,内存泄漏在过去是更加常见的问题。你只能通过修复代码才能真正的对付它们。因此,一个更好的方法是将回收不用的内存的工作自动化,整体消除可能产生的人为错误。这个自动化操作叫做垃圾回收(简称GC)。

智能指针

一种自动化操作的方式是通过使用析构函数。例如,我们使用C++中的向量vector做上一个例子中同样的事,它会在离开其作用域时自动的调用其析构函数:

int send_request() {
size_t n = read_size();
vector<int> elements = vector<int>(n); if(read_elements(elements.size(), &elements[0]) < n) {
return -1;
} return 0;
}

但是再更复杂的情况中,尤其是在多个线程中共享对象时,仅仅使用析构函数并不够。这就引出了垃圾回收最简单的形式:引用计数。对每个对象,你只需要知道它有多少次被引用到,并且当这个计数达到零时这个对象就可以安全的被回收。一个众所周知的例子是C++中的共享指针shared pointers

int send_request() {
size_t n = read_size();
auto elements = make_shared<vector<int>>(); // read elements store_in_cache(elements); // process elements further return 0;
}

现在,为了避免下次函数调用时再去读这个元素elements,我们可能想要缓存他们。在这个例子中,就不能再当向量vector离开作用域时销毁它。因此,我们使用共享指针shared_ptr,它记录引用到它的数量,当你使用它传值时计数增加,同时当它离开作用域时计数减少。一旦引用的计数降到零,共享指针shared_ptr就会自动地删除下面的向量。

自动化内存管理

在上面的C++代码中,我们仍然要显式地指出我们要何时关心内存管理。但是如果我们能让所有对象都按这种方式运行呢?那将会十分方便,因为开发者不再需要在用完后考虑清理工作。运行时环境会自动知道某些内存不再使用,然后进行清理,也就是自动回收垃圾。最早的垃圾收集器是在1959年为Listp语言创建的,从那时起这项技术才开始不断前进。

引用计数

我们上面演示的C++中共享指针的想法可以应用到所有对象。许多编程语言,比如Perl、Python和PHP都采用了这个方法。下面一张图片可以很好地阐述它:

绿色的云朵表明它们指向的对象仍在被程序员使用。严格的说,它们可能是当前执行方法中的本地变量或静态变量等。这会随着不同的编程语言而不同,所以我们这里先不关注它。

蓝色的圆圈是内存中活着的对象,其中的数字表示它们被引用的次数。最后,灰色的圆圈是没有被明确使用中的对象(那些直接被绿色云朵引用到的)所引用到的对象。这些灰色的对象是就是垃圾,可以被垃圾收集器清理。

这看起来真的很好,不是么?好吧,确实如此,但是整个方法有个巨大的缺陷。它很容易被一些对象形成的分离的循环终结,这些对象都已不在使用范围但是由于循环引用,因此它们的引用计数都不是零。举例来说:

看到么?红色的对象实际上是程序不再使用的垃圾。但是由于引用计数的限制,仍然有内存泄漏。

有几种办法可以克服这个缺陷,比如使用特殊的'弱引用'或对循环使用单独的算法。前面提到的编程语言-Perl、Python和PHP,都用了一种或另一种方法处理循环,这超出了本手册的范围。相反,我们开始更详细地研究JVM所使用的方法。

标记和清除

首先,JVM对于对象的可达性构成更加具体。与我们之前看到绿色云朵的含糊定义相反,我们有一个非常详细和明确的一组对象,被称为是垃圾回收根源(GC Roots)。

  • 本地变量
  • 活动线程
  • 静态字段
  • JNI引用

JVM用来追踪所有可达(活着)的对象和确保不可达对象使用的内存能被重用的方法被称为标记和清除算法。它由两个步骤构成:

  • 标记阶段 遍历所有可达对象,从垃圾回收根源开始并在本地内存中记录一个所有这些对象的分类账本。
  • 清除阶段 确保不可达对象使用的内存可以在下次内存分配时重用。

JVM中不同的垃圾回收算法,比如并行清扫、并行标记+复制CMS 在实现这些阶段时略有不同,但是从概念上都有着同上述两个步骤相似的过程。

该关方法的一个至关重要的情况是循环不再产生内存泄露:

不太好的方面是情况是应用线程需要在回收发生时暂停,因为如果引用在不停的变化,你将无法真正的计算出引用的数量。这种临时暂停应用程序以便于JVM专心做清理工作的情况被称为停止世界暂停。它们会因很多原因产生,但是垃圾收集是至今最普遍的一个。

Java垃圾回收手册翻译 - 什么是垃圾回收的更多相关文章

  1. java虚拟机学习-JVM调优总结-垃圾回收面临的问题(8)

    如何区分垃圾 上面说到的“引用计数”法,通过统计控制生成对象和删除对象时的引用数来判断.垃圾回收程序收集计数为0的对象即可.但是这种方法无法解决循环引用.所以,后来实现的垃圾判断算法中,都是从程序运行 ...

  2. 【转载】Java性能优化之JVM GC(垃圾回收机制)

    文章来源:https://zhuanlan.zhihu.com/p/25539690 Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我 ...

  3. Java性能优化之JVM GC(垃圾回收机制)

    Java的性能优化,整理出一篇文章,供以后温故知新. JVM GC(垃圾回收机制) 在学习Java GC 之前,我们需要记住一个单词:stop-the-world .它会在任何一种GC算法中发生.st ...

  4. Java内存模型(JMM)以及 垃圾回收机制 小结

    内存模型: 内存模型描述了程序中各个变量(实例域.静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节,对象最终是存储在内存里面的,这点没有错,但是编译 ...

  5. 【深入理解Java虚拟机】自动内存管理机制——垃圾回收机制

      Java与C++之间有一堵有内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来.C/C++程序员既拥有每一个对象的所有权,同时也担负着每一个对象生 ...

  6. 你必须了解的java内存管理机制(四)-垃圾回收

    本文在个人技术博客不同步发布,详情可用力戳 亦可扫描屏幕右侧二维码关注个人公众号,公众号内有个人联系方式,等你来撩... 相关链接(注:文章讲解JVM以Hotspot虚拟机为例,jdk版本为1.8) ...

  7. Java虚拟机详解(三)------垃圾回收

    如果对C++这门语言熟悉的人,再来看Java,就会发现这两者对垃圾(内存)回收的策略有很大的不同. C++:垃圾回收很重要,我们必须要自己来回收!!! Java:垃圾回收很重要,我们必须交给系统来帮我 ...

  8. java面试官最爱问的垃圾回收机制,这位阿里P7大佬分析的属实到位

    前言 JVM 内存模型一共包括三个部分: 堆 ( Java代码可及的 Java堆 和 JVM自身使用的方法区). 栈 ( 服务Java方法的虚拟机栈 和 服务Native方法的本地方法栈 ) 保证程序 ...

  9. Java GC系列(3):垃圾回收器种类

    本文由 ImportNew - 好好先生 翻译自 javapapers. 目录 垃圾回收介绍 垃圾回收是如何工作的? 垃圾回收的类别 垃圾回收监视和分析 在这篇教程中我们将学习几种现有的垃圾回收器.在 ...

随机推荐

  1. API文档自动生成,Swagger的配置

    ASP.NET的部署方式 第一步:引用程序集 打开NuGet程序包管理器,搜索Swagger,安装第一个,注意画圈的地方, 已经包含主程序和UI了,安装完成后会在根目录App_Start文件夹下生成S ...

  2. Centos 7 最小化时间服务部署配置

    基本原理 Centos 7 我所了解有两种时间服务,NTPD与chronyd:两者对Centos 7 的支持都很好,有对chrony非常夸赞的,不过我这里只讲ntpd:有对chrony有想法的可以自行 ...

  3. Nginx(3)---代理与负载均衡

    一.代理简述 代理分为正向代理和反向代理, 正向代理:客户端与目标服务器之间增加一个代理服务器,客户端直接访问代理服务器,在由代理服务器访问目标服务器并返回客户端并返回 .比如夜深人静的时候访问的一些 ...

  4. Spring(五)核心容器 - 注册 Bean、BeanDefinitionRegistry 简介

    目录 前言 正文 1.BeanDefinitionRegistry 简介 2.registerBeanDefinition 方法注册 Bean 最后 前言 上篇文章我们对 BeanDefinition ...

  5. ICC中对Skew进行Debug的好工具--Interactive CTS Window

    本文转自:自己的微信公众号<集成电路设计及EDA教程> ​以后打算交替着推送多种EDA工具的教程而不只是单纯针对某个工具,依次来满足不同粉丝的需求. 这里分享一篇多年之前写的推文,虽然时间 ...

  6. eclipse编写代码所遇到的问题

    spring方面: 1.Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListabl ...

  7. LUA学习笔记(第18-20章)

    数学库 print(math.pi)-->π print(math.huge)-->Lua中表示的最大数字 --[[ 3.1415926535898 1.#INF ]] print(mat ...

  8. Java中类的关系

    在java里类的关系大致分为三种, 1.继承(a is b):继承extends,实现implement 2.包含(a has b):组合>聚合>关联.关系亲密度越来越小,一个类在另一个类 ...

  9. 实例探究Aspectj,解析SentinelResourceAspect

    为了学习SentinelResourceAspect,这篇文章里我用Aspectj实现一个AOP实例,一起来看下. Sentinel 提供了 @SentinelResource 注解用于定义资源,支持 ...

  10. iOS - 一个简单的带标题的图标的实现

    代码不复杂,直接上代码: ImageViewButton.h // // ImageViewButton.h// // 带有图片.底部标题或者顶部的按钮 // // #import <UIKit ...