以下无特殊说明,都是针对于HotSpot虚拟机。
GC的定义
垃圾回收,也就是在我们JVM程序中新创建的不作为任何用途去使用的对象。
怎么确认是垃圾?
可达性分析算法: 如果某个对象到达"GC Roots"根对象没有任何引用链相关联,也就是从GC Roots到这个对象不可达,则该对象被认为不可能再被使用。
GC Roots对象包含:虚拟机栈中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象等等。另外还有“计数算法”,感兴趣的朋友可以自行了解。
强弱软虚
按照可达性算法,没有GC Roots的引用就是垃圾,这样对于程序设计来说,有点太过绝对,尤其是我们需要保留这些垃圾在内存足够的时候不回收,在特别紧张的时候才回收(常见如:缓存数据),JDK1.2后,扩展了引用的概念,主要包含以下四类:
1:强引用
是程序中普遍存在的引用赋值(比如:Object o = new Object()),这种对象,垃圾回收器永远不会回收。
2:软引用
用来描述一些还有用,但并非必须的对象。
系统将要发生内存溢出的时候,会对这些对象进行回收,如果这次回收还异常,才会报内存异常。(jdk1.2提供SoftReference来实现软引用)
3:弱引用
它的强度比软引用更弱,它是在垃圾回收开始工作,无论内存是否足够,都会被回收掉。(jdk1.2提供WeakReference来实现)
4:虚引用
是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用,主要是为了能在这个对象被收集器回收时能收到一个系统通知。
finalize()方法
对象可达性分析后发现没有“GC Roots”的引用链后,会被第一次标记为可回收对象,随后会进行一次筛选,筛选此对象是否有必要执行“finalize()”方法,如果有就放置到一个名为 “F-Queue”的队列中,最后由一条JVM创建的、低调度优先级的线程去执行他们的"finalize()"方法,并且这里的执行是触发这个方法开始运行,并不承诺一定等待它运行结束(因为如果某个对象执行缓慢,甚至死循环,会导致FQueue其他对象无法运行,甚至导致整个内存回收子系统的崩溃),并且任何一个对象的finalize()调用只有一次。
总之不推荐使用,因为不会管它是否允许结束,很不稳定。
方法区的垃圾
必须同时满足这三个条件,才被允许回收:
1:该类的所有实例已被回收。
2:加载该类的类加载器已被回收。
3:该类对应的java.lang.class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
在大量反射、动态代理、CGlib等字节码框架,动态生成JSP及OSGi这类频繁自定义类加载器的场景中,需要对类卸载的能力。
常用的垃圾回收器与调优参数
Serial回收器
新生代单线程回收器,在它回收的时候,必须暂停所有工作线程。
-XX:+UseSerialGC
ParNew回收器
新生代的Serial的多线程并行版本
-XX:+UseParNewGC
Parallel Scavenge回收器
新生代,能多线程的收集器,他比其他收集器不同的是, 他目标是达到一个可控的吞吐量的目标。(开发设定一个堆内存大小后,其他的就设置停顿时间和吞吐量大小就好了,其他的都自适应)
-XX:+UseParallelGC -XX:+UseAdaptiveSizePolicy 打开自适应GC调节策略 -XX:MaxGCPauseMills 设置最大垃圾收集停顿时间,会根据这个值调整Java堆大小或者其他一些参数 -XX:GCTimeRatio 设置吞吐量大小,值是0-100的整数,如果设置的值是N,那么系统将花费不超过1/(1+n)的时间用于垃圾收集。
Serial Old回收器
老年代的Serial收集器,标记整理算法。
Parallel Old回收器
Parallel Scavenge的老年代版本(调优参数可参照上面ParallelGC)
-XX:+UseParallelOldGC
CMS回收器
老年代的,以获取最短回收停顿时间为目标的“标记清除”算法收集器。
优点:
并发低停顿收集器。
缺点:
1:产生浮动垃圾。GC的时候由新对象加入。
2:由于是标记清除算法,会产生空间碎片,需要配置处理。
3:会占用一部分线程资源而导致应用程序变慢,CMS默认启动的回收线程数是=(处理器核心线程数+3)/4,也就是25%的影响,如果处理器核心线程数不足4个的时候就对用户程序影响很大了。
-XX:+UseConcMarkSweepGC
-XX:ParallelGCThreads
CMS回收线程=(ParallecGCtThreads+3)/4
-XX:ParallelCMSThreads
CMS垃圾回收线程数
-XX:CMSInitiatingOccupancyFraction
当老年代空间使用率达到多少时,进行一次CMS垃圾回收,默认是68
-XX:+UseCMSCompactAtFullCollection
CMS垃圾回收后,进行一次内存碎片整理
-XX:CMSFullGCsBeforeCompation
多少次CMS回收后,进行一次内存压缩
-XX:CMSClassUnloadingEnabled
使用CMS进行元空间回收
-XX:CMSInitiatingPermOccupancyFraction
达到什么比例进行Perm回收
G1回收器
新型的垃圾回收器类别,思路是
1:优先级式的回收垃圾,而不是 一次性把整个JAVA堆回收干净
2:总体上看是“标记-整理”算法,局部是“标记-复制算法”,总之这两种算法在运行期间都不会产生内存空间碎片。
缺点:
1:G1的卡表更复杂,堆中每个Region都有一份卡表,这导致G1的记忆集占整个堆空间的20%甚至更多。
2:使用写屏障维护G1卡表,会增加更多的运算资源。
-XX:+UseG1GC
-XX:+G1HeapRegionSize
分区大小
-XX:G1NewSizePercent
新生代最小比例,默认5%
-XX:G1MaxNewSizePercent
新生代最大比例,默认60%
-XX:MaxGCPauseMills
最大GC停顿时间,如果任何一次停顿超过这个值,G1就会尝试调整新老年代的比例、调整堆大小、调整景升年龄等首都按,一般建议200-300MS。
-XX:ParallelGCThreads
设置并行回收,GC的工作线程数量=(ParallelGCThreads + 3)/4。
-XX:ConcGCThreads
设置并行回收,GC的工作线程数量。
-XX:InitiatingHeapOccupancyPercent
整个堆使用率达到多少时,执行并发标记周期。
ZGC
它把G1里面的卡表存在引用里面,染色指针。
1:能降低内存的损耗。
2:减少内存屏障的使用数量
3:一个某个region存活对象被移走,这个Region立即就能被释放和重用掉,而不用等待整个堆中指向该Region的引用修正后才能清理,它能"自愈"(并发重分配)。
4:性能,吞吐量目前都优于G1和Parallel Scavenge回收器。
缺点:
因为ZGC没有分代概念,虽然STW很短,但是整个执行过程还是很长,在这个过程中JAVA对象可能会创建大量新对象,这些对象只能变成浮动垃圾,等待下次回收,如果回收掉的空间持续小于产生的浮动垃圾,那可分配的空间会越来越少(似乎还是要分代?),目前的方法是尽可能增大堆空间。否则会FULLGC
-XX:+UseZGC
-XX:ZFramentationLimit
设置ZGC触发的堆碎片阈值
-XX:ZCollectionInterval
指定ZGC的回收间隔(单位:秒),有助于控制回收频率。
-XX:+UseNUMA
启用NUMA支持
-XX:ZAllocationSpikeTolerance
控制ZGC对内存分配突发情况的容忍度,数值越高,适应能力越强
其他JVM参数
常用调优参数
- -Xmn -Xms -Xmx -Xss 年轻代 最小堆 最大堆 栈空间
- -XX:+UseTLAB 使用TLAB,默认打开
- -XX:+PrintTLAB 打印TLAB的使用情况
- -XX:TLABSize 设置TLAB大小(TLAB (Thread Local Allocation Buffer,线程本地分配缓冲区)是 Java 中内存分配的一个概念,它是在 Java 堆中划分出来的针对每个线程的内存区域,专门在该区域为该线程创建的对象分配内存。它的主要目的是在多线程并发环境下需要进行内存分配的时候,减少线程之间对于内存分配区域的竞争,加速内存分配的速度。TLAB 本质上还是在 Java 堆中的,因此在 TLAB 区域的对象,也可以被其他线程访问。)
- -XX:+DisableExplictGC System.gc()不管用 ,FGC
- -XX:+PrintGC
- -XX:+PrintGCDetails
- -XX:+PrintHeapAtGC
- -XX:+PrintGCTimeStamps
- -XX:+PrintGCApplicationConcurrentTime (低) 打印应用程序时间
- -XX:+PrintGCApplicationStoppedTime (低) 打印暂停时长
- -XX:+PrintReferenceGC (重要性低) 记录回收了多少种不同引用类型的引用
- -verbose:class 类加载详细过程
- -XX:+PrintVMOptions
- -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial 必须会用
- -Xloggc:opt/log/gc.log
- -XX:MaxTenuringThreshold 升代年龄,最大值15
- 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 ... 这些不建议设置
跟踪 GC 日志
- -XX:+PrintGC:最简单的 GC 参数,每一行代表进行了一次 GC。
- -XX:+PrintGCDetails:最常用的 GC 参数,可以在日志中打印 GC 详细信息。
- -XX:+PrintHeapAtGC:详细的打印出 GC 前后的堆内存信息。
- -XX:+PrintGCTimeStamps:输出每次 GC 发生的时间
- -XX:+PrintGCApplicationConcurrentTime:记录 Java 程序执行时间,表示应用程序在两次垃圾回收之间运行多长时间
- -XX:+PrintGCApplicationStoppedTime:记录 Java 程序因为 GC 而产生的停顿时间
跟踪类加载/卸载信息
- -verbose:class:跟踪类加载和卸载
跟踪查看虚拟机参数
-
-XX:+PrintVMOptions:查看 Java 程序启动时设置的参数。
-
-XX:+PrintCommandLineFlags:打印虚拟机的显示和隐藏参数。
-
-XX:+PrintFlagsFinal:打印所有系统参数的值。