1 前言

GC(Garbage Collect)是jvm对于内存管理的核心功能,正是因为它才让Java程序员从内存释放的苦海中脱离出来,所以作为一个程序员都有必要去了解一下他的原理。

说一句题外话,我曾经被问到GC的具体实现,那个时候我就知道一些基本的思想,结果被人鄙视了。对于这个问题我到现在仍保留个人观点,就算java用了很久,如果不涉及到java程序的性能调优,GC其实也不用钻那么深。但是GC的思想中积累了很多的智慧,真是不得不去好好领略一下。苦于原来一直找不到一个比较系统的GC资料,我这里做一个相对系统一点的介绍,希望对后来人有帮助。

2 内存划分

说道GC,我们必须先要了解一下jvm的内存空间是如何划分的,本文只以Sun JDK为例,其划分为Permanent Generation(又称方法区、持久代)、Heap(堆)、Native Method Stack、JVM Method Stack、PC register。

Permanet Generation中是要加载类的相关信息,包括方法信息、静态变量等。如果超出指定大小,会抛出OutOfMemory的错误。

Heap,用于存对象实例和数值,分为New Generation和Old Generation。前者用户存储新生成的对象,后者用于存放多次GC后仍然存活的对象。如果超出指定大小,会抛出OutOfMemory的错误。

JVM Method Stack、PC register是随着线程创建的,他们只会占用操作系统内存或寄存器,由于线程私有,所以性能很高。如果超出指定大小,会抛出StackOverflowError的错误。

Native Method Stack在Sun JDK的实现中是和JVM Method Stack放在一起的。

3 内存分配

如果java要使用内存,第一步当然是要获取内存,这是由jvm进行分配的。上面说过了,新建对象都是在Heap的New Generation上的,而Heap又是所有线程共享的,可以想象必须要有锁机制才能保证安全。但是这当然会带来效率问题,所以这里有个优化,就是每个线程会分配一个TLAB(Thread Local Allocation Buffer),这个是线程独享的,当然给的空间也很小。默认是优先在TLAB上分配,所以为什么写小对象对java程序而言更合理。还有一种优化这里也说一下,就是jvm会根据实际运行情况进行分析,如果逃逸分析正好发现方法中的变量会被外部读取,那么就可以直接在Stack上分配,压根不用过Heap了。

4 GC算法

这里才是重头戏,看看具体GC的算法吧。其实没什么高深的内容,就是遇到的实际问题来找对应的解决办法。

4.1 引用计数法

简单的想一下,如果一个对象没有被任何人引用,那么他就要被回收。这个实现起来很简单,但是会有一个问题。如果有两个对象互相引用,但是没有任何其他对象引用他们,那么他们就会造成资源泄漏。所以实际使用时还是有些问题的,只能适合一些简单引用的场景。

4.2 跟踪收集法

就是把整个引用想象成一个允许有环路的树结构,但是根节点只有一个,然后从根节点出发去查看对象是否可达。但这个就是要程序暂停,来保证一次扫描的现场不变。

4.2.1 Copying

开辟另外一个内存空间,把扫描到可达的对象复制过去,然后把原内存空间全部清除即可。适用于存活对象较少的情况。

4.2.2 Mark-Sweep

把扫描到可达的对象都标记下来,然后把所有未标记的对象清除。但这个方法会引起内存碎片。适用于存活对象较多的情况。

4.2.3 Mark-Compact

在Mark-Sweep基础上,在把内存空间整理一下,让存储连续以消除内存碎片。

5 jvm实现

学了这么多基础知识,看看Sun JDK是怎么做的吧。

先搞清楚jvm到底要对什么进行GC。上面说的几种需要用内存的地方,Native Method Stack、JVM Method Stack、PC register是用的操作系统内存,用完就直接释放了,不需要我们多操心。Permanet Generation是加载类的相关信息,考虑到有动态加载,这个地方还是有可能需要GC的。Heap是对象生成和存活的土壤,这当然是GC的主要目标。

我们还是先解释两个名词,还记得上面说的New Generation和Old Generation吗?为什么要把Heap划分成这两类?原因就是他们所存的对象有完全不一样的特征,就是存活时间。因此我们自然想到要使用不一样GC策略。对于New Generation的GC叫做Minor GC,对于他们两个一起的GC叫做Full GC。再补充一个常识,就是New Generation通常不会太大,而Old Generation会比较大。所以一定要注意,Full GC的开销非常大,是要尽量避免的。

5.1 New Generation的GC策略

Serial GC。采用单线程方式,用Copying算法。到这里我们再来说说为什么New Generation会再次被划分成Eden Space和S0、S1,相信聪明的你一定已经想到Copying算法所需要的额外内存空间了吧,S0和S1又称为From Space和To Space。具体细节自己好好想想。

Parallel Scavenge。将内存空间分段来使用多线程,也是用Copying算法。

ParNew。比Parallel Scavenge多做了与Old Generation使用CMS GC一起发生时的特殊处理。

5.2 Old Generation的GC策略

Serial GC。当然也是单线程方式,但是实现是将Mark-Sweep和Mark-Compact结合了下,做了点改进。

Parallel Mark-Sweep、Parallel Mark-Compact。同样也是把Old Generation空间进行划分成regions,只是粒度更细了。为什么用这两个算法,不用我赘述了吧。

CMS(Concurrent Mark-Sweep) GC。我承认这个GC我真的没怎么看懂,目的是为了实现并发,结果就造成具体实现太麻烦了。有兴趣的朋友去看书吧,文末我说了是哪本书。这里有个地方可以说一下,就是算法使用的还是Mark-Sweep,对于内存碎片的问题,CMS提供了一个内存碎片的整理功能,会在执行几次Full GC以后执行一次。

6 如何使用

知道jvm怎么做的,那我们怎么用呢?这才是最实际的问题。其实每种GC方式都可以在启动时用参数指定,具体还是去看书。我提一下client和server模式。默认情况下是client模式,但是这个看机器配置自动选择,说了我估计你也记不住,用的话还是显示声明比较好。比较有意思的是这两种模式就可以认为用户所对应的不同场景,因此也会给出不一样的GC策略。具体如下表:

+----------------------------------------------------+

|        |     New Gen GC    |     Old Gen GC        |

+--------+-------------------------------------------+

| client | Serial GC         | Serial GC             |

+--------+-------------------------------------------+

| server | Parallel Scavenge | Parallel Mark-Sweep GC|

+--------+-------------------------------------------+

JVM的GC策略的更多相关文章

  1. 【转】JVM 分代GC策略分析

    我们以Sun HotSpot VM来进行分析,首先应该知道,如果我们没有指定任何GC策略的时候,JVM默认使用的GC策略.Java虚拟机是按照分代的方式来回收垃圾空间,我们应该知道,垃圾回收主要是针对 ...

  2. 关于JVM内存模型,GC策略以及类加载器的思考

    JVM内存模型 Sun在2006年将Oracle JDK开源最终形成了Open JDK项目,两者在绝大部分的代码上都保持一致.JVM的内存模型是围绕着原子性(操作有且仅有一个结果).可见性(racin ...

  3. 深入学习重点分析java基础---第一章:深入理解jvm(java虚拟机) 第一节 java内存模型及gc策略

    身为一个java程序员如果只会使用而不知原理称其为初级java程序员,知晓原理而升中级.融会贯通则为高级 作为有一个有技术追求的人,应当利用业余时间及零碎时间了解原理 近期在看深入理解java虚拟机 ...

  4. JVM内存模型以及HotSpot的GC策略

    概述 想要进一步掌握Java语言,必须要深入了解一下Java程序的运行环境.本文会对JVM的内存模型.Java内存自动管理机制.以及Oracle官方虚拟机HotSpot在GC方面的实现策略进行大概的梳 ...

  5. JVM 分代GC策略分析

    JVM 分代GC策略分析   我们以Sun HotSpot VM来进行分析,首先应该知道,如果我们没有指定任何GC策略的时候,JVM默认使用的GC策略.Java虚拟机是按照分代的方式来回收垃圾空间,我 ...

  6. 第一周JVM核心技术-工具与GC策略

    一. JDK工具 1.1 内置命令行工具 工具 简介 jps/jinfo 查看java进程 jstat 查看JVM内部GC信息 jmap 查看JVM堆或类占用空间信息 jstack 查看线程信息 jc ...

  7. HBase的几种调优(GC策略,flush,compact,split)

    一:GC的调优 1.jvm的内存 新生代:存活时间较短,一般存储刚生成的一些对象 老年代:存活时间较长,主要存储在应用程序中生命周期较长的对象 永久代:一般存储meta和class的信息 2.GC策略 ...

  8. 082 HBase的几种调优(GC策略,flush,compact,split)

    一:GC的调优 1.jvm的内存 新生代:存活时间较短,一般存储刚生成的一些对象 老年代:存活时间较长,主要存储在应用程序中生命周期较长的对象 永久代:一般存储meta和class的信息 2.GC策略 ...

  9. Spark学习之路 (十四)SparkCore的调优之资源调优JVM的GC垃圾收集器

    一.概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本 ...

随机推荐

  1. 人生苦短,我用python

    星空不问赶路人,时光不负有心人,你可以脱变. 1.计算机的初步认识 2.解释器的安装 python2.7(2020年官方不在维护) python3.6 (官方推荐) 1.下载安装包 https://w ...

  2. 20155207王雪纯 Exp2 后门原理与实践

    20155207王雪纯 Exp2 后门原理与实践 实验步骤 一.windows获取Linux shell Windows:使用 ipconfig 命令查看当前机器IP地址. 进入ncat所在文件地址, ...

  3. 2017-2018-1 20155216 《信息安全系统设计基础》 实现mypwd

    2017-2018-1 20155216 <信息安全系统设计基础> 实现mypwd 作业要求: 1.学习pwd命令 2.研究pwd实现需要的系统调用(man -k; grep),写出伪代码 ...

  4. .Net Core使用Unity替换原生DI

    原文:.Net Core使用Unity替换原生DI 一.DIP.IOC.DI 面对对象设计原则可以帮助我们开发出更好的程序,其中有一个依赖倒置原则DIP并由此引申出IOC.DI等概念.就先粗略的了解一 ...

  5. 6 开发工具IDE-pycharm

    http://www.cnblogs.com/sean-yao/p/8321034.html 激活码:http://blog.csdn.net/qq_39248703/article/details/ ...

  6. 使用LINQ的Skip和Take函数分批获取数据

    Skip函数和Take函数是System.Linq对类Enumberable的扩展, 其中Skip函数是跳过序列中的前n个数据,参数为需要跳过的数据量, Take函数是取序列中的n个数据,参数为要获取 ...

  7. [CQOI2012]组装 贪心

    [CQOI2012]组装 贪心好题. LG传送门 首先有一个必须要能推的式子:设第\(i\)种零件选的生产车间位置为\(x _ i\),组装车间位置为\(x\), 则总的花费为 \[f(x) = \s ...

  8. 一个简单的nodejs项目(cat-names)分析

    https://github.com/sindresorhus/cat-names 一个非常简单的nodejs项目,用来方便的获取猫猫的名字: 安装: npm install --save cat-n ...

  9. [SYZOJ279]滑♂稽♂树

    主♂席♂树♂裸♂题 https://syzoj.com/problem/279 https://oj.changjun.com.cn/problem/detail/pid/2425 // It is ...

  10. Oracle安装到Maven本地仓库

    1.由于Maven的特性,并且之前的IDE环境已帮我们集成了Maven.而现在我们需要手动安装MVN本地仓库到电脑. 将mvn绿色安装包bin路径配置到系统环境变量Path中 验证命令: mvn –v ...