垃圾收集器和垃圾回收算法的关系?
垃圾算法(引用计数,复制算法,标记清除,标记整理)都是内存回收的方法论,垃圾收集器是这种算法的落地实现。
主要发生在方法区和堆中,其中方法区在jdk1.8中的实现是元空间(Metaspace)
发生在新生代中的GC,被称为Young GC(Scavenge GC,Minor GC),
新生代中的对象大多数生命周期比较短,所以这里的GC发生很频繁。
Full GC,指发生在老年代的 GC,出现了 Full GC 一般会伴随着至少一次的 Young GC(老
年代的对象大部分是Young GC 过程中从新生代进入老年代),比如:分配担保失败。Full
GC 的速度一般会比 Young GC 慢 10 倍以上。
GC在运行的时候,暂停整个应用就叫STW。
并发这里只的是GC线程和用户线程一起运行,GC可能占用用户的线程。
串行:垃圾回收的时候只有一个GC线程工作。
并行:垃圾回收的时候有多个GC线程一起工作。
在单核的情况下,并行可能更慢。
单线程收集
收集时必须Stop The Word(停止所有的用户线程)
jvm内存不大时停顿时间短(说明堆内存大小影响GC的效率)
配置jvm的时候只需要配置年轻代的收集器即可,jvm会自动选择匹配的老年代收集器。
开启后会使用:年轻代使用Serial +老年代使用的是Serial Old 的收集器组合,说明都是使用的串行。
使用的算法为:年轻代使用复制算法,老年代使用标记-整理算法;
多线程收集(是Serial的多线程版本)
单cpu的效率不及串行收集器Serial,多cpu肯定快于Serial
收集的过程中还是会暂停其他的用户线程。
可以控制ParNew的运行线程数,默认情况下线程数量和CPU核数的数目相同。
启用ParNew收集器只会影响年轻代的手机,不影响老年代。
开启后会使用:年轻代使用ParNew收集器, 老年代使用Serial Old 收集器,
注意:jdk8已经不推荐这么组合了!因为Serial Old要被弃用了。
使用的算法为:年轻代使用复制算法,老年代使用标记-整理算法;
-XX:UseParallelGCThreads这个参数可以设置并行收集的线程数。(CPU>8 设置N=5/8 CPU<8 设置Cpu实际个数)
读音:[?p?r?lel]
- ParNew在新生代是并行,对应的老年代的却是串行,但是Parallel Scavenge 是并行的。
多线程并发收集器,吞吐量优先。
这个收集器通常用于程序运算任务,不需要太多的交互任务。
自适应调节策略也是ParallelScavenge收集器与ParNew收集器的一个重要区别
(虚拟机会根据当前系统的运行情况,动态调整参数设置一个最合适的停顿时间或者最大吞吐量 -XX:MaxGCPauseMillis 这个参数可以设置)
一句话概括:新生代和老年代都使用并行收集。
前面的Serial以及ParNew年轻代的收集器对应的老年代的收集器都是Serial Old都是串行的,这个收集器对应的老年代收集器是并行的(Parallel Old)
什么是系统吞吐量
系统吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
系统吞吐量越接近1吞吐量越高。假设运行用户代码的时间为1000ms,垃圾收集时间为100ms,那这个吞吐量结果为:0.90
开启后会使用:年轻代使用Parallel Scavenge收集器, 老年代使用Parallel Old收集器,
使用的算法为:年轻代使用复制算法,老年代使用标记-整理算法;
-XX:UseParallelGCThreads这个参数可以设置并行收集的线程数。
特征
单线程收集器
使用的是标记整理算法(可以处理空间碎片问题)
它和年轻代的Serial以及ParNew配合使用,它被作为老年代收集器CMS的备选收集方案。
注意:jdk8已经放弃使用了!
CMS是一种以获取最短回收停顿时间为目标的收集器(CMS又称多并发低暂停的收集器)
适用于互联网站或者B/S系统的服务器上,因为GC的停顿时间短,响应速度就快了!
并发收集所以停顿时间短 (注意:这里要区别上面的并行和串行,并发收集指的是GC线程和用户线程是同时运行的。)
采用标记-清除算法(会产生内存碎片问题,执行几次cms会进行一次整理)
- 初始标记(CMS initial mark)
只是标记一下GC Roots能直接关联的对象,并没有把真个引用链标记完整,速度很快,但是需要暂停用户线程。- 并发标记(CMS concurrent mark: GC Roots Tracing 过程)
在这个过程中根据初始标记的对象会把整个对象引用链标记处理,不需要暂停用户线程,和用户线程一起运行- 重新标记(CMS remark)
为了修正在并发标记期间,产生变动对象的那一部分记录,仍然需要暂停用户线程。- 并发清除(CMS concurrent sweep: 已死对象将会就地释放, 注意:此处没有压缩)
清除GC Roots不可达对象,不需要暂停用户线程。- 由于耗时最长的并发标记和并发清除过程,都是和用户线程并发执行的,所以总体上来说CMS收集器和用户线程是并发执行的。
- 并发执行对CPU资源压力大
由于并发进行,CMS在收集与用户线程会同时增加对堆内存的占用,也就是说CMS必须要在老年代对堆内存用尽之前完成一次垃圾回收,否则CMS回收失败时,
将触发担保机制,串行老年代收集器(Serial Old)将会以STW的方式进行一次GC,从而造成较大停顿。- 无法处理浮动垃圾(在并发清除阶段用户线程产生的新的垃圾)
- CMS是一款“标记--清除”算法实现的收集器,容易出现大量空间碎片,最后不得不通过担保机制对堆内存进行整理压缩。
我们可以通过参数配置经过多少次CMS才进行一次的整理压缩。
参数:-XX:CMSFullGCsBeForeCompaction(默认为0,每次都进行内存整理压缩)
开启该参数会自动将-XX:UseParNewGC打开;
开启后会使用:年轻代使用ParNew 老年代使用CMS+Serial Old(担保)
使用的算法为:年轻代使用复制算法,老年代使用标记-清除,标记-整理
使用命令:java -XX:+PrintCommandLineFlags -version
我们在配置垃圾收集器的时候只需要指明新生代或者老年代的收集器即可,jvm会自动选择老年代或者新生代的收集器
下面是设置年轻代的GC收集器
下面是设置老年代的GC收集器
bool Arguments::check_gc_consistency() {
bool status = true;
uint i = 0;
if (UseSerialGC) i++;
if (UseConcMarkSweepGC || UseParNewGC) i++;
if (UseParallelGC || UseParallelOldGC) i++;
if (UseG1GC) i++;
if (i > 1) {
jio_fprintf(defaultStream::error_stream(),
"Conflicting collector combinations in option list; "
"please refer to the release notes for the combinations "
"allowed\n");
status = false;
}
return status;
使用jps查出进程,使用jinfo打印运行的jvm参数 jinfo -flags PID
可以使用Jconsole,jvisualvm可视化工具进行查看
在项目启动的时候添加JVM参数:-XX:+PrintCommandLineFlags 也可以获得。
这个图和上面的jvm总体结构图没有冲突,这里只是强调一个线程在运行方法的时候涉及到的内存。
决定这个变量或者对象实例是在栈中分配还是堆中分配,逃逸指的是这个变量或者对象实例的作用域是否超过当前方法(逃出了方法)。
如果一个对象实例仅仅在当前方法中使用,并没有通过【返回值返回出去】或者 【通过调用别的方法时作为参数传递过去】
那么这个对象就是没有发生逃逸,可以将这个对象分配在java栈空间中,栈空间是随着方法的执行结束而清理的,不需要GC,所以可以提高效率。
jvm中逃逸分析是可以设置开关的,默认是开启的。
https://blog.csdn.net/qq906627950/article/details/81324825
https://www.cnblogs.com/rgever/p/8902210.html
原文:https://www.cnblogs.com/wangsen/p/11119777.html