Java垃圾回收入门(上集)

photo by street.classic instagram

Java等语言的一大特性就是能够自动管理内存——即能自动垃圾回收。它作为语言的一大特性,给予了开发者很大的便利,但在某些情景下也会成为我们程序的瓶颈,这里就以java的GC作为例子,我和大家一起分析下它的背景、优劣势分析、适用的场景,常见的回收算法,回收算法中的关键部分、原理和具体实现,最后还会以各个语言已有的实现为例来相互做个对比。

01 垃圾回收算法的背景

首先,自动垃圾回收这个概念最早是从Lisp语言出来的,创建的初衷是自动处理内存管理。那么为什么会需要程序来进行内存管理呢?

写过C的同学都知道,在写代码时,最常出现的一个问题就是内存问题,比如代码中申请了空间却忘了释放,导致内存泄漏。或是指针已经释放过一次了,结果再别处又被释放了一次,造成“double free";有一次调研显示,对于C程序,60%以上的bug都是程序员对内存操作不当的问题。可见人工管理内存会的确是一件容易出错的事儿。

02 tradeOff

垃圾回收诞生后,极大地降低了程序的bug率,程序员们可以更专注在业务功能的编写上,以往经常出现的这些问题都逐渐减弱了:

  1. 野指针问题 指针所对应的的内存空间已经被释放了,然而还有引用指向它,如果这块地址被重新分配后,再访问这个地址时,会发现这是一片乱数据。

  2. double free问题 通常是发生在尝试多次释放同一块内存,即使那块内存已经被释放;

  3. 内存泄漏 通常发生于尝试去释放某块内存,但这块内存被一个无法访问的对象占用了。

但带来这些便利的同时也带来了弊端:

  1. 垃圾回收相比人工的释放内存方式,要消耗一些计算资源来决定释放哪些内存。
  2. 在性能上对程序有损耗。
  3. 在回收时会暂停程序,来进行回收。这对于实时性要求比较高的、事务性要求较高的系统,是一件无法容忍的事。
  4. 垃圾回收的时机会有延迟,如果回收的比较晚,可能会发生内存泄漏

当Java程序遇到并发比较大的情况,自动垃圾回收的速度没法及时跟上,可能会出现性能问题。所以在jvm调优时,垃圾回收也是调优的一大重点。

我们先看看一个对象是如何从创建到被回收的:

1.假设现在要new一个爸爸类的对象, new 类名:
class Father{
  int action;
 ...
}

在业务代码中这么写:

public Father newFather(){
   Father obj1= new Father(); 
}
2.在编译过后,jvm虚拟机会先去常量区去寻找这个类的符号引用。如果没有,说明这个类还没被加载,则开始这个类的解析和初始化
3.jvm在新生代新建一个对象 obj1
image.png
3.在新生代中存活

随着新建的对象越来越多,Eden代已经放不下了,要发生一次回收和整理,于是发生了minor GC,jvm会从S0/S1中,选取一个空闲的,将存活下来的对象整整齐齐地拷过去,于是新生代又腾出了空间。

4.晋升老年代

如果我们的对象obj1还在被使用着,那么每一次新生代回收后都存活下来,年龄会不断+1,当他年龄增长到一定程度(默认是15次)还存活的话,就晋升到老年代


image.png
5.被回收

obj1已经没有人引用,成为一个待回收的“垃圾”,此时程序要再将一些大对象放入老年代,这会发现老年代内存不够,需要发生一次MajorGC,以便腾出内存来分配给对象。

说完了过程,我们来看看这里面的关键部分:

1.新建对象的内存分配

java将堆划分为新生代和老年代,而新生代里面又区分了Eden、FromSurvivor、ToSurvivor这几个区域,其中,toSurvivor的区域是空的。


图片来自https://blog.csdn.net/suifeng629/article/details/82462164

默认情况下,jvm采取的是一种动态分配的策略,根据生成对象的速率、Survivor区的使用情况动态调整Eden区和Survivor区的大小。前面的例子是在堆中分配了一个实例的空间。

当我们new一个实例的时候,其实是在新生代里面分配了一块内存。由于堆空间是线程共享的,所以进行空间的划分也需要进行同步,否则就容易出现两个对象共用一个内存的事故。极客时间的专栏中有个很好的比方:这就相当于两个司机(线程)要停在不同的停车位,不然都往一个地方停就很容易出现剐蹭事故。

java虚拟机给出了解决方案:预先为每个线程分配相应的堆空间,该线程只允许使用分给自己的堆空间。这项技术称为TLab,jvm默认开启了这选项。具体每个线程都可以申请一段属于自己的连续内存,线程需要维护两个指针,一个在TLab空闲内存的起始位置,一个在TLab尾部,接下来new操作时,就可以通过指针加法来实现,只要TLab的在完成指针加法后,空闲内存所在的指针依旧小于尾部,那么就分配成功,否则,则需要线程申请新的TLab。

当Eden区的空间逐渐被耗尽,就会发生一次Minor GC,回收新生代的垃圾对象,然后将存活的对象从From Survivor区拷贝到To Survivor区,交换from和to区的指针,以保证下次to区是空的。而当对象在新生代存活的年龄达到15,或是Survivor区的对象占用率为50%,较高复制次数的对象将会晋升到老年代里。

MinorGC有什么好处?好处就是不用对整个堆进行回收。但是,这也会有一个问题,老年代可能引用了新生代的对象,在标记存活对象时,我们需要扫描老年代的对象,如果该对象拥有对新对象的引用,那么这个引用也被视为GCRoot。这样一来,感觉又做了一次全堆扫描?

jvm对此给出了一个解决方案:卡表,具体大家可以去详细了解下。

2.jvm的堆内存是划分为新生代和老年代,那么为什么要进行分代?

其实这是基于一个假设而造就的:大部分java对象只存活一段时间,而少数对象则会存活一段时间。

Oracle的郑雨迪曾写过一个基准测试,验证了这一假设,结果如下图:


image.png

可看出短期存活的对象远远多于长期存活的对象。jvm根据对象存活的周期不同,对不同代采用不同的回收算法进行回收,从而提高回收效率。
对于新生代,我们可采用耗时较短的回收策略;而对于老年代,由于我们可以猜测这会在新生代的垃圾已经被回收了,或是堆本身的空间也被用完了,这会jvm将会进行一次全堆扫描,不计耗时成本。

在oracle官网上,你也可以看到采取分代的原因:Why Generational Garbage Collection?

了解完对象是如何在内存中创建和存活后,下一篇我们来看下如何实现无用对象的回收。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容