JVM的重要性不言而喻了,若是把java的应用程序比做一辆跑车,那么JVM就是这辆车的发动机,没有它,java程序就成了空中楼阁,无根浮萍。而在JVM中有一块内存区域叫作运行时数据区域,存储了运行时所须要的全部对象,而Heap Area则是其中最大的一块。java
内存毕竟不是无限的,因此就须要一种机制来将再也不使用的对象进行回收,这种机制就是今天咱们要讲的GC。程序员
更多精彩内容且看:算法
小师妹:F师兄,你相信这个世界有轮回吗?spring
师兄我是一个坚决的无神论者,活在当下就行了,何须操心后面的轮回呢?并发
小师妹:F师兄,这个你就不懂了,意识是组成脑的原子群的一种组合模式,咱们大脑的物质基础和一块石头没有什么不一样。当咱们掌握大脑的组合方式,而后重构,咱们的意识就重现了,这就是轮回。这但是量子理论中提到的观念哦。jvm
哇,小师妹何时这么厉害了,都开始探讨这么高深的话题了。F师兄我实在是跟不上节奏啊。spring-boot
小师妹,F师兄,我是怕你尴尬,想引出java对象的生命周期这个话题嘛。性能
量子理论我不熟,java对象我还没怕过谁。区块链
对象的生命周期其实很简单:建立,使用中,最后被销毁。spa
举个最简单的建立对象的例子:
Object obj = new Object();
对象建立的时候,将会为该对象分配特定的空间。
对象建立以后,就能够被其余的对象使用,若是其余的对象有使用该对象,那么咱们成为该对象被引用了。
当一个对象没有被其余对象引用的时候,咱们就称为该对象能够被回收了。在Java中,对象的回收是由GC来负责的。
小师妹:F师兄,我以为垃圾回收好像挺简单的,咱们为每一个对象维持一个指针计数器,每引用一次就加一,这样不就能够实现垃圾回收器了吗?
底层原理是这么一个道理,可是JVM须要一种更加高效的算法来保证垃圾回收的效率,同时也不会影响正在运行的程序。
接下来咱们将会介绍一下,在JVM中比较经常使用几个垃圾回收算法:
Mark and sweep是最最简单的垃圾回收算法,简单点讲,它能够分为两个步骤:
标记live对象听起来很简单,就是扫描堆中的对象,看这些对象是否被引入。
可是这里有一个问题,若是是两个对象互相引用的时候,而这两个对象实际上并无被外部的对象所引用,那么这两个对象实际上是应该被回收的。因此咱们还须要解决一个关键性的问题:从哪里开始扫描的问题。
JVM定义了一些Root对象,从这些对象开始,找出他们引用的对象,组成一个对象图。全部在这个图里面的对象都是有效的对象,反之不在对象图中的对象就应该被回收。有效的对象将会被Mark为alive。
这些Root对象包括:正在执行的方法中的本地对象和输入参数。活动的线程,加载类中的static字段和JNI引用。
注意,这种遍历实际上是有个缺点的,由于为了找到对象图中哪些对象是live的,必须暂停整个应用程序,让对象变成静止状态,这样才能构建有效的对象图。后面咱们会介绍更加有效的垃圾回收算法。
扫描对象以后,咱们就能够将未标记的对象删除了。
删除有三种方式,第一种方式是正常删除。可是正常删除会致使内存碎片的产生。因此第二种方式就是删除以后进行压缩,以减小内存碎片。还有一种方式叫作删除拷贝,也就是说将alive的对象拷贝到新的内存区域,这样一样能够解决内存碎片的问题。
在讲CMS以前,咱们先讲一下垃圾回收器中的Eden,Old和Survivor space几个你们应该都很熟悉的分代技术。
Young Gen被划分为1个Eden Space和2个Suvivor Space。当对象刚刚被建立的时候,是放在Eden space。垃圾回收的时候,会扫描Eden Space和一个Suvivor Space。若是在垃圾回收的时候发现Eden Space中的对象仍然有效,则会将其复制到另一个Suvivor Space。
就这样不断的扫描,最后通过屡次扫描发现任然有效的对象会被放入Old Gen表示其生命周期比较长,能够减小垃圾回收时间。
以后要将的几个垃圾回收器,除了ZGC,其余都使用的是分代的技术。
好了,如今继续讲CMS,CMS是mark and swap的升级版本,它使用多个线程来对heap区域进行扫描,从而提高效率。
CMS在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是mark-sweep。
使用CMS的命令很简单:
-XX:+UseConcMarkSweepGC
上面是列出的一些CMS的调优参数。
Serial garbage collection使用单一的线程来进行垃圾回收操做,其好处就是不须要和其余的线程进行交互。若是你是单核的CPU,那么最好就是选择Serial garbage collection,由于你不能充分利用多核的好处。一样的它也经常用在比较小型的项目中。
Serial garbage collection在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是 mark-sweep-compact。
下面是开启命令:
-XX:+UseSerialGC
和serial GC相似,它在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是 mark-sweep-compact。不一样的是它是并行的。
能够经过下面的命令来指定并发的线程:
-XX:ParallelGCThreads=N
若是你是多核处理器,那么Parallel GC多是你的选择。
Parallel GC是JDK8中的默认GC。而在JDK9以后, G1是默认的GC。
使用下面的命令来开启Parallel GC:
-XX:+UseParallelGC
为何叫G1呢,G1=Garbage First,它是为替换CMS而生的,最先出如今java7中。
G1将heap区域划分红为多个更小的区域,每一个小区域都被标记成为young generation 或者old generation。从而运行GC在更小的范围里运行,而不是影响整个heap区域。
可使用下面的命令来开启:
-XX:+UseG1GC
ZGC是一个可扩展的,低延迟的GC。ZGC是并发的,并且不须要中止正在运行的线程。
使用下面的命令来开启:
-XX:+UseZGC
ZGC是在JDK11中被引入的。
小师妹:F师兄,你讲了这么多个GC,到底我该用哪一个呢?
高射炮不能用来打蚊子,因此选择合适的GC才是最终要的。这里F师兄给你几个建议:
本文介绍了几种GC的算法,你们能够根据须要选用。
本文做者:flydean程序那些事本文连接:http://www.flydean.com/jvm-gc-algorithms/
本文来源:flydean的博客
欢迎关注个人公众号:程序那些事,更多精彩等着您!