垃圾回收的主要算法:
- 引用计数器
- 可达性分析算法
回收算法
-
标记清除(Mark-Sweep)
产生空间碎片
-
复制算法(Copying)
浪费内存空间
-
标记整理(mark-Compact)
没有碎片,相对较慢(压缩内存的时间)
区别:标记清除 位置不连续 产生碎片
复制算法 没有碎片 浪费空间
标记整理 没有碎片 效率较低
JVM内存模型
分代模型:jdk1.8以及以前版本
分区模型:jdk1.9以及以后版本
将堆内存分为一小部分一小部分的区域进行管理;
年轻代和老年代
年轻代
- 年轻代的中间的垃圾回收器是串行回收,就是单线程处理;
- 第三列是并行回收,效率较快
- 第一列的ParNew是对第三列的并行回收进行优化,最重要的是下面老年代的CMS垃圾回收器。
老年代
- G1垃圾回收器是jdk1.7提出的效率快的垃圾回收机制;在jdk1.9时设置为java GC 的默认垃圾回收器,取代CMS;
- java9默认号称STW时间不超过10ms;
- ZGC:STW时间1ms
-
应用程序中会一直存活的对象:
- 经历过15次GC没被干掉的对象:
- 数据库连接池
- Spring的bean
- 缓存对象
- 经历过15次GC没被干掉的对象:
-
大对象(配比可以设置 ,根据堆内存的大小)
- 占用内存比较大的情况下,直接放入老年代
垃圾回收线程
-
年轻代时Yougth GC 操作速度快,STW时间短,对应用影响不大;
-
老年代满了就是开启full GC ,对全部堆内存进行全部GC ,STW时间长,影响用户体验度。
年轻代基本都是复制算法,老年代基本都是标记整理算法
栈内存的分配
年轻代和老年代的内存占比为1:2
年轻代分为伊甸园区和幸存区(Survivor);幸存区分为S0和S1
Stop-the-World,简称STW
指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应, 有点像卡死的感觉,这个停顿称为STW。
-
STW事件和采用哪个GC方式无关,所有的GC都有这个事件。
-
哪怕是G1也不能完全避免STW情况的发生,只能说垃圾回收器越来越优秀,回收效率越来越高,尽可能地缩短了暂停时间。
-
STW是JVM在后台自动发起和自动完成的。在用户不可见的情况下,把用户正常的工作线程全部停掉。开发中不要用System.gc() ;会导致STW的发生。
年轻代的STW时间少,可以忽略不计,尽量避免Full GC的出现。
常用的垃圾收集器
串行操作回收
都会产生STW时间
Serial: 新生代采取复制算法,暂停所有用户进程
Serial Old: 老年代采取的标记整理算法,暂停所有用户进程
并行操作回收
依然产生STW
Paralldl Scavenge
Paralldl Old
ParNew基本和Paralldl Scavenge 相同
ParNew的出现是Parallel的升级版,和CMS搭配使用,更好减少STW时间
CMS
(ConcurrentMarkUPSweep)
作用于老年代,有四大核心标志
- 初始标记:有STW产生(短暂),标记Root
- 并发标记:不会产生STW
- 重新标记:处理错标和漏标(浮动垃圾)
- 并发清理:
浮动垃圾:
因为在并发标记的时候时不产生STW,可能会有进程产生状态的变换或者是新对象的产生或者别的情况。可能会在并发标记是产生。
产生因为在CMS情况有并发处理,标记和用户进程会同时运行,难免有新的对象产生或者新的垃圾生成;
处理方法是在CMS的重新标记时候产生STW在对所有的对象进行重新标记。
G1:目前比较好的垃圾回收机制
面向服务端的应用的垃圾回收器
jvisualvm 命令
装插件
1、找到新的更新地址 visualvm新访问地址: 进入“Plugins”
https://visualvm.github.io/pluginscenters.html
找到对应自己JDK版本的更新地址
2、进入jvisualvm的插件管理 “工具” - “插件” 在"设置"中修改url地址为刚才我们在github上找到的对应我们JDK版本的地址
修改成功后,可用插件即可刷新出来
3、安装VisualGC插件
4、重启即可看到Visual GC
注:相应的jdk的bin目录下有的jvisualvm .exe文件