聊到垃圾回收,首先咱们需要了解对象在堆的分布情况,这里只是正常情况(比如大对象,内存溢出等)
JVM内存分代模型
部分垃圾回收器使用的模型
除Epsilon ZGC Shenandoah 之外的GC都是使用逻辑分代模型
G1时逻辑分代,物理不分代
除此之外不仅逻辑分代,而且物理分代
新生代+老年代+永久代(1.7)/元数据区(1.8)Metaspace
1、永久代 元数据 - Class
2、永久代必须指定大小限制,元数据可以设置,也可以不设置,无上限(受限于物理内存)
3、字符串常量 1.7-永久代,1.8-堆
4、MethodArea逻辑概念-永久代、元数据
ps:
新生代和老年代属于堆,永久代和元数据区属于 元数据
新生代=Eden+2个suvivor区
1、YGC回收之后,大多数的对象被回收,活着的进入S0
2、再次YGC,活着的对象eden + S0 -> S1
3、再次YGC eden + S1 -> S0
4、年龄足够 -> 老年代(年龄 :CMS6,其他15)
5、s区装不下 -> 老年代
老年代
1、顽固分子
2、老年代满了FGC Full GC
对象何时进入老年代:
超过 XX:MaxTenuringThreshold指定次数(YGC)
--Parallel Scavenge 15
-- CMS 6
-- G1 15
PS:存放GC年的空间为4bit,即最大为15,调整的时候不能超过15。
动态年龄
-- S0 -> S1超过50%:YGC后 eden和s0经过垃圾清理后把存活的对象放到S1中,这时,超过S1空间50%的对象,按照标记次数最大的放入Old区
-- 把年龄最大放入Old区
GC Tuning(Generation)
1、尽量减少FGC
2、MinorGC = YGC
3、MajorGC = FGC
栈上分配
1、线程私有小对象
2、无逃逸:只在当前使用
3、支持标量替换:就是一个简单的对象,使用普通类型替换
--无需调整:不需要调整参数
-XX:-DoEscapeAnalysis
-XX:-EliminateAllocations
-XX:-UseTLAB
默认开启,开启后效率变高,较少后效率变低,所以能在栈上分配空间就别在堆上分配,堆上需要垃圾回收介入,效率要低
线程本地分配
Thread Local Allocation Buffer
为了解决多线程竞争问题,每个线程在eden默认分配1%的空间,这个空间不会造成线程竞争
1、占用eden,默认1%
2、多线程的时候不用竞争eden就可以申请空间,提高效率
3、小对象
垃圾回收算法
常见的垃圾回收算法有三种,了解垃圾回收算法之前需要了解一个知识点,怎么识别是垃圾对象
标记垃圾
标记垃圾有两种方式
1、引用计数
2、Root Searching (根搜索)
什么是根?线程栈变量,静态变量,常量池,JNI指针
which instances are roots? JVM stack,natice method stack,run-time constant pool,static refrences in method area ,Clazz
Mark-Sweep(标记清除)
优点
1、算法相对简单
2、存活对象比较多的情况下效率较高
缺点
1、两遍扫描,效率偏低(第一遍进行标记,找到无法被回收的对象,第二遍进行清除未被标记的对象)
2、容易产生碎片
Copying(拷贝)
优点
1、适用于存活对象较少的情况
2、只扫描一次,效率较高
3、没有碎片
缺点
1、空间浪费
2、移动复制对象,需要调整对象引用
Mark-Compact (标记压缩或者标记整理)
优点
1、不会产生碎片,方便对象分配
2、不会产生内存减半
缺点
1、扫描两次
2、需要移动对象,效率偏低