本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
- 众所周知,Java区别于C、C++语言的其中一个小点,就是Java的内存释放(也就是无用对象的清理)是JVM替我们程序员做的。
- 又因为这部分代码是C、C++语言写好的,对我们Java程序员来说就多了几分神秘色彩。
- Java语言一直在进化,除了语法功能之外,就是这个JVM的进化,而JVM的进化就不的不提起这个垃圾收集器的不断增进。
垃圾收集器是什么
顾名思义,垃圾收集器就是Java程序运行起来后,一些对象经过类加载分配内存经调用栈使用后,这个对象没用啦,这个对象就成了垃圾,对象成为垃圾不要紧,为对象分配的那些内存空间,可是宝贵的资源,所以要清理掉这些垃圾对象从而释放内存,进而为新生的对象所使用。(Java是多么环保)
垃圾回收器怎么工作
不妨你来设计一下垃圾回收器,你会做怎样的考虑:
- 什么样的对象被判定为垃圾对象?
- 这些垃圾对象和非垃圾对象如何区分?
- Java程序一直在运行怎么保证清理垃圾的时候又产生出新的垃圾?
- 用什么算法实现以上步骤,最为省时省空间? ......
JVM垃圾回收器怎么做的
针对上面提出的问题,JVM垃圾回收器都有自己的实现:
- Java中对象间是引用关系,那么没有被引用的对象,就是垃圾对象。
- Java有颜色标记、指针着色等标记这些垃圾对象/非垃圾对象,做以区分。
- Java运用了暂停全部线程、并发标记、读写屏障等的手段,来标记清除对象,且周而复始。
- Java垃圾收集器有可达性分析算法、标记整理算法、标记复制算法、标记清除等等。
JVM垃圾收集器的进化史
第一代产品:Serial收集器(JDK1.5前)
- 此时JVM内存模型有分代的概念(年轻代、老年代)
- Serial负责年轻代、Serial Old负责老年代的垃圾收集清理。
- Serial使用标记复制、Serial Old使用标记整理。
- 单线程简单高效,清理垃圾时暂停所有线程(STW)只保留GC线程。
第二代产品:Parallel Scavenge收集器
- 此时JVM内存模型有分代的概念(年轻代、老年代)
- Parallel Scavenge负责年轻代、Parallel Scavenge Old负责老年代的垃圾收集清理。
- Parallel Scavenge使用标记复制、Parallel Scavenge Old使用标记整理。
- 多线程,CPU利用率高,清理垃圾时暂停所有线程只保留GC线程。
第三代产品:Concurrent Mark Sweep(CMS)
- 此时JVM内存模型有分代的概念(年轻代、老年代)
- ParNew / Serial负责年轻代、CMS负责老年代的垃圾收集清理。
- CMS使用标记清理:有了三色标记算法,并发标记,并发清理等。
- 多线程,CPU利用率高,产生多处内存碎片。
第四代产品:Garbage-First (G1)
- 弱化年轻、老年分代概念,区分eden region、old region、servivor region、humongous
- 根据用户期望时间计算region回收价值和成本并排序
- 局部标记复制算法,整体标记整理。
- 针对大内存,指定STW时间。
第五代产品:ZGC
- 没有分代概念,用region概念
- 从以往对象头中标记对象,改为指针中保存垃圾标记信息
- 使用颜色指针+读屏障+映射转发形式,标记复制,标记整理都使用
- STW不超过1ms