垃圾回收基础
什么是垃圾
没有任何引用指向的一个对象或多个对象(循环引用)叫做垃圾。
如何定位垃圾
引用计数:引用计数为0的对象标记为垃圾,无法解决循环依赖
GC roots:
JVM stack, native method stack, runtime constant pool, static references in method area, Clazz
从GC roots开始遍历所有对象,遍历不到的对象标记为垃圾
常见的垃圾回收算法
这里说垃圾回收算法,其实叫垃圾回收思想更合适,因为实际垃圾回收算法都是借鉴了这些思想。
标记清除(Mark Sweep):找到所有垃圾后清除
优点是实现简单,缺点是容易产生内存碎片。
拷贝(Copying):将内存一分为二,只使用其中一份,垃圾回收时,把非垃圾对象拷贝到另一份内存,然后把第一份内存清掉
优点是没有内存碎片,缺点是浪费空间。
标记压缩(Mark Compact):找到所有垃圾后清除,然后把存活对象压缩到一起消除碎片
优点是没有内存碎片,缺点是效率偏低。
JVM内存分代模型
jdk1.7:年轻代(Young)、老年代(Old)、永久代(Perm)
jdk1.8:年轻代(Young)、老年代(Old)、元数据区(MetaData)
元数据区可指定大小的下限,但不可指定上限,除非整个JVM用尽,否则不会溢出
>jdk1.8:JVM内存不再是分代模型
永久代必须指定大小的上下限,也就是说有永久代溢出的可能性;元数据区可以设置也可以不设置,无上限(受限于物理内存)
字符串常量:1.7放在永久代,1.8放在堆
方法区:逻辑概念,1.7放在永久代,1.8放在元数据区
年轻代:
Eden + Suvivor0 + Suvivor1,空间比例8:1:1(年轻代总空间为10)
new的对象放在Eden区,如果Eden空间不够直接进入老年代
YGC后,Eden大多数对象都会被回收,存活的对象拷贝至S0
再次YGC后,Eden+S0中存活的对象被拷贝到S1
再次YGC后,Eden+S1中存活的对象被拷贝到S0
……
每次YGC后,存活的对象的“年龄”+1,年龄够了会升入老年代
老年代:
存放“顽固分子”,或者太大的对象
老年代满了会触发FGC,GC调优的目的就是减少FGC的次数,因为FGC可能会有较长时间的STW(Stop The World)
常见的垃圾回收器
上图红色细虚线表示相互连接的2个垃圾回收器可以配合使用
用于年轻代的垃圾回收器:
Serial:单线程回收
Parallel Scavenge:多线程回收
ParNew:为了与CMS配合使用而从Parallel Scavenge改造而来的,多线程回收
用于老年代的垃圾回收器:
Serial Old:单线程回收
Parallel Old:多线程回收
CMS:ConcurrentMarkSweep,这里Concurrent是指垃圾回收线程与应用线程在某些阶段可以并行执行,从而减少STW时间(官方声称STW时间<=200ms)
使用非分代模型的JVM的垃圾回收器:
G1:STW<=10ms
ZGC:STW<=1ms
Shenandoah:Shenandoah与ZGC都是下一代JVM备选的垃圾回收器
不用关注:
Epsilon:jdk内部调试使用的回收器
常见参数
java -XX:+PrintFlagsFinal 打印出jvm所有配置的默认值和当前值
你会发现有=和:=,=就是默认值,:=就是你手动修改后的值,或者是jvm根据你当前系统智能设定的值
:=
denotes that the flag value was overriden either by JVM ergonomics or manually via a command line option.
|
|
-Xms10240m jvm初始内存
-Xmx10240m jvm最大内存
-XX:MaxPermSize=512m 永久代最大空间
-XX:+PrintGCDetails 在gc.log打印GC信息
-Xloggc:\
-XX:+PrintGCTimeStamps 打印gc时间信息
rmi远程控制gc,设置为固定2小时触发一次full gc
-Dsun.rmi.dgc.client.gcInterval=7200000
-Dsun.rmi.dgc.server.gcInterval=7200000
正常情况下是由jvm控制gc,但是如果开启rmi,则由rmi接管gc的控制