码迷,mamicode.com
首页 > 其他好文 > 详细

JVM的GC策略

时间:2018-01-18 17:04:30      阅读:142      评论:0      收藏:0      [点我收藏+]

标签:机制   mem   空间   parallel   csdn   目标   调优   方法区   意思   

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策略

标签:机制   mem   空间   parallel   csdn   目标   调优   方法区   意思   

原文地址:https://www.cnblogs.com/xiarongjin/p/8310157.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!