https://blog.csdn.net/qq_22993855/article/details/106948129
JVM
新生代收集器:Serial(单线程,复制算法,简单高效)、ParNew(多线程版serial,唯一一个能与CMS收集器配合工作)、Parallel Scavenge(吞吐量优先,复制算法,多线程并行)
老年代收集器:CMS(标记-整理)、Serial Old(老年代的Serial,标记-整理)、Parallel Old(多线程,标记-整理)
整堆收集器: G1
G1收集器
一款面向服务端应用的垃圾收集器。
特点如下:
并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续运行。
分代收集:G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
空间整合:G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。
可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒
https://www.cnblogs.com/chenpt/p/9803298.html
如何选择垃圾收集器
1、单CPU或者小内存,单机程序 — -XX:+UseSerialGC
2、多CPU,需要大吞吐量,如后台计算型应用
-XX:+UseParallelGC + -XX:+UseParallelOldGC
3、多CPU,追求低停顿时间,快速响应如互联网应用
-XX:+UseParNewGC + -XX:+UseConcMarkSweepGC
内存碎片问题
由于CMS老年代使用标记-清除回收策略,因此会有内存碎片问题。当碎片过多时,将会给大对象分配带来麻烦,往往会出现老年代还有很多空间但就已经不能保存对象了。不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了-XX:UseCMSCompactAtFullCollection开关参数,用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程。 有参数可以配置有多少次Full GC会堆内存碎片进行整理(-XX:CMSFullGCsBeforeCompaction)
Jvm
永久代 元空间 使用本地内存 存放类的元数据
垃圾回收算法
新生代(Young)分为Eden区,From区与To区 ,复制算法(快)
创建一个对象的时候,总是在Eden区操作当,Eden区满的时候,会触发第一次young gc,把还活着的对象拷贝到Survivor From区;当Eden区再次触发young gc的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域,并将Eden和From区域清空。,如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区
8:1:1 好处:提升内存利用率,新生代朝生夕灭的特点,活下来的大概就是10%
缺点:Eden:Survivor1:Survivor2比例为8:1:1,少了10%的可用内存。
老年代 MajorGC采用标记—清除算法:
内存碎片化严重,后续可能发生大对象不能找到可利用空间的问题。
首先扫描一次所有老年代,标记出存活的对象
然后回收没有标记的对象。
标记-整理算法(Mark-Compact)
该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
缺点:效率比较低,当内存中存活对象多,并且都是一些微小对象,而垃圾对象少时,要移动大量的存活对象才能换取少量的内存空间。
垃圾判断算法
1 引用计数法
给每个对象添加一个计数器,当有地方引用该对象时计数器加1,当引用失效时计数器减1。用对象计数器是否为0来判断对象是否可被回收。缺点:无法解决循环引用的问题。
2 可达性分析算法
通过GC ROOT的对象作为搜索起始点,通过引用向下搜索,所走过的路径称为引用链。通过对象是否有到达引用链的路径来判断对象是否可被回收(可作为GC ROOT的对象:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI引用的对象)
JVM 安全点
https://blog.csdn.net/weixin_34319999/article/details/92030169
GC的目的是帮助我们回收不再使用的内存,在多线程环境下这种回收将会变得非常复杂,要安全地回收需要满足一下两个条件:
1.堆内存的变化是受控制的,最好所有的线程全部停止。
2.堆中的对象是已知的,不存在不再使用的对象很难找到或者找不到即堆中的对象状态都是可知的。
为了准确安全地回收内存,JVM是在Safe Point点时才进行回收
如何使线程中断,一般有两种方式:主动式和被动式。主动式JVM设置一个全局变量,线程去按照某种策略检查这个变量一旦发现是Safe Point就主动挂起,被动式就是发个信号,例如关机、Control+C,带来的问题就是不可控,发信号的时候不知道线程处于什么状态
安全区域
安全点完美的解决了如何进入GC问题,实际情况可能比这个更复杂,但是如果程序长时间不执行,比如线程调用的sleep方法,这时候程序无法响应JVM中断请求这时候线程无法到达安全点,显然JVM也不可能等待程序唤醒,这时候就需要安全区域了。
安全区域是指一段代码片中,引用关系不会发生变化,在这个区域任何地方GC都是安全的,安全区域可以看做是安全点的一个扩展。线程执行到安全区域的代码时,首先标识自己进入了安全区域,这样GC时就不用管进入安全区域的线层了,线层要离开安全区域时就检查JVM是否完成了GC Roots枚举,如果完成就继续执行,如果没有完成就等待直到收到可以安全离开的信号。