深刻理解Java虚拟机——Java内存区域——史上最烂的图文并茂结合
深刻理解Java虚拟机——Java垃圾回收器——史上最烂的图文并茂结合java
垃圾回收机制不定时,向堆内存清理不可达对象
误区:不可达的对象并不会立刻就会被直接回收,而是至少要通过两次标记的过程。 第一次被标记过的对象,会检查该对象是否重写了finalize() 方法。若是重写了该方法,则将其放入一个F-Query队列中,不然,直接将对象加入“即将回收留集合。在第二次标记以前,F-Query队列中的全部对象会逐个执行finalize() 方法,可是不保证该队列中全部对象的finalize() 方法都能被执行,这是由于JVM建立一个低优先级的线程去运行此队列中的方法,极可能在没有遍历完以前,就已经被剥夺了运行的权利。web
那么运行finalize()方法的意义何在呢?
这是对象避免本身被清理的最后手段,若是在执行finalize() 方法的过程当中 , 使得此对象从新与GG Roots引用链相连,则会在第二次标记过程当中将此对象从F-Query队列中清除,避免在此次回收中被清除,恢复成了一个“正常”的对象。 但显然这种好事不能无限的发生,对于曾经执行过一次finalize()的对象来讲,以后若是再被标记,则不会再执行finalize()方法,只能等待被清除的命运。 以后,GC将对F-Queue中的对象进行第二次小规模的标记,将队列中从新与GC Roots引用链恢复链接的对象清除出“即将回收”集合。全部此集合中的内容将被回收。算法
咱们举一个例子来简单说明上述的意思多线程
public class DustbinSave { public static void main(String[] args) { // TODO Auto-generated method stub //初始化堆 DustbinSave dustbinSave=new DustbinSave(); //dustbinSave=null; dustbinSave=null; System.gc();//手动回收垃圾 } @Override protected void finalize() throws Throwable{ //gc回收垃圾以前调用 System.out.println("垃圾回收机制以前。..调用"); } }
有了垃圾回收机制咱们来了解下如何判断对象为垃圾对象并发
在对象值添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值就+1,当引用失效的时候,计数器的值就-1,任什么时候刻计数器都为0的对象就是再也不被使用的,垃圾收集器将回收该对象使用的内存。
ide
咱们来写一段代码来验证是不是使用引用计数法来判断为垃圾对象svg
public class Main { private Object instance; public Main() { //byte[] bytes=new byte[20*1024*1024]; } public static void main(String[] args) { // TODO Auto-generated method stub Main main=new Main(); Main main2=new Main(); main.instance=main2; main2.instance=main; main=null; main2=null; System.gc(); ////parallel垃圾回收器 } }
原理图
运行结果:
性能
-verbose:gc
-xx:+PrintGCDetail
必须配置上面两个参数,才能够打印出垃圾回收的日志信息优化
原理图
那些地方能够做为GCRoots的对象?spa
知道如何判断谁为垃圾对象后,咱们就要知道怎么清除它,因此讲到垃圾回收算法
java虚拟机内存结构
线程共享区
线程独占区
java虚拟机内存结构中的堆内存可继续划分
复制算法含义:
S0和s1将可用内存按容量分红大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另外一块内存上去,而后把使用过的内存空间一次清理掉。这样使得每次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂状况,只须要移动堆顶指针,按顺序分配内存便可,实现简单,运行高效。
复制算法的缺点显而易见,可以使用的内存降为原来一半。
复制算法用于在新生代垃圾回收
复制算法原理图
上面形成的问题:内存浪费
改进后的算法
复制算法
含义:
原理:
垃圾回收的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器能够更高效的执行,大部分状况下,会要求系统进行一个停顿的状态。停顿的目的是为了终止全部的应用线程,只有这样的系统才不会有新垃圾的产生。同时停顿保证了系统状态在某一个瞬间的一致性,也有利于更好的标记垃圾对象。所以在垃圾回收时,都会产生应用程序的停顿。
原理图
原理图
老年代ParallelOldGC回收器也是一种多线程的回收器,和新生代的
ParallelGC回收器同样,也是一种关往吞吐量的回收器,他使用了标记压缩
算法进行实现。
-XX:+UseParallelOldGC 进行设置
-XX:+ParallelCThread也能够设置垃圾收集时的线程教量。
复制算法
多线程垃圾回收器
达到可控制吞吐量
-XX:MaxGFPauseMillis 垃圾收集器停顿时间1
-XX:CGTimeRatio 吞吐量大小
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。CMS收集器是基于“标记-清除”算法实现的,整个收集过程大体分为4个步骤:
初始标记(CMS initial mark)
并发标记(CMS concurrenr mark)
从新标记(CMS remark)
并发清除(CMS concurrent sweep)
其中初始标记、从新标记这两个步骤任然须要停顿其余用户线程。初始标记仅仅只是标记出GC ROOTS能直接关联到的对象,速度很快,并发标记阶段是进行GC ROOTS 根搜索算法阶段,会断定对象是否存活。而从新标记阶段则是为了修正并发标记期间,因用户程序继续运行而致使标记产生变更的那一部分对象的标记记录,这个阶段的停顿时间会被初始标记阶段稍长,但比并发标记阶段要短。
CMS收集器对CPU资源很是敏感。在并发阶段,虽然不会致使用户线程停顿,可是会占用CPU资源而致使引用程序变慢,总吞吐量降低。CMS默认启动的回收线程数是:(CPU数量+3) / 4。
CMS收集器没法处理浮动垃圾,可能出现“Concurrent Mode Failure“,失败后而致使另外一次Full GC的产生。因为CMS并发清理阶段用户线程还在运行,伴随程序的运行自热会有新的垃圾不断产生,这一部分垃圾出如今标记过程以后,CMS没法在本次收集中处理它们,只好留待下一次GC时将其清理掉。这一部分垃圾称为“浮动垃圾”。也是因为在垃圾收集阶段用户线程还须要运行,即须要预留足够的内存空间给用户线程使用,所以CMS收集器不能像其余收集器那样等到老年代几乎彻底被填满了再进行收集,须要预留一部份内存空间提供并发收集时的程序运做使用。
在默认设置下,CMS收集器在老年代使用了68%的空间时就会被激活,也能够经过参数 -XX:CMSInitiatingOccupancyFraction的值来提供触发百分比,以下降内存回收次数提升性能。要是CMS运行期间预留的内存没法知足程序其余线程须要,就会出现“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来从新进行老年代的垃圾收集,这样停顿时间就很长了。因此说参数-XX:CMSInitiatingOccupancyFraction设置的太高将会很容易致使“Concurrent Mode Failure”失败,性能反而下降。
CMS是基于“标记-清除”算法实现的收集器,使用“标记-清除”算法收集后,会产生大量碎片。空间碎片太多时,将会给对象分配带来不少麻烦,好比说大对象,内存空间找不到连续的空间来分配不得不提早触发一次Full GC。为了解决这个问题,CMS收集器提供了一个 -XX:UseCMSCompactAtFullCollection开关参数 ,用于在Full GC以后增长一个碎片整理过程,还可经过 -XX:CMSFullGCBeforeCompaction参数设置执行多少次不压缩的Full GC以后,跟着来一次碎片整理过程。
原理
G1回收器(Garbage-First)实在]dk1.7中提出的垃圾回收器,从长期目标来看是为了取代CMS回收器,G1回收器拥有独特的垃圾回收策略,G1属于分代垃圾回收器,区分新生代和老年代,依然有eden和from/to区,它并不要求整个eden区或者新生代、老年代的空间都连续,它使用了分区算法。
并行性: G1回收期间可多线程同时工做。
井发性G1拥有与应用程序交替执行能力,部分工做可与应用程序同时执行,在整个
GC期间不会彻底阻塞应用程序。
分代GC:G1依然是一个分代的收集器,可是它是非两新生代和老年代一杯政的杂尊。
空间基理,G1在国收过程当中,不会微CMS那样在若千tacAy 要进行碎片整理。
G1
来用了有效复制对象的方式,减小空间碎片。
利得程,用于分区的缘由,G能够贝造取都分区城进行回收,帽小了国收的格想,
提高了性能。
使用.XXX:+UseG1GC 应用G1收集器,
Mills指定最大停顿时间
使用-XX:MaxGCPausel
设置并行回收的线程数量
使用-XX:ParallelGCThreads
优势:
步骤: