福州seo按天扣费,成都网络推广优化,做一个网站成本大概多少钱,重庆丰都建设局网站1 先谈Finalize()finalize()能做的所有工作#xff0c;使用try-finally或者其他方式都可以做得更好、更及时#xff0c;所以笔者建议大家完全可以忘掉Java语言中有这个方法的存在。——《深入理解JVM》finalize()方法确实可以实现一次对象的自救#xff0c;但是其不确定性和…1 先谈Finalize()finalize()能做的所有工作使用try-finally或者其他方式都可以做得更好、更及时所以笔者建议大家完全可以忘掉Java语言中有这个方法的存在。——《深入理解JVM》finalize()方法确实可以实现一次对象的自救但是其不确定性和昂贵的运行代价都表明这个方法的使用需要十分的慎重。那么finalize()在什么时期起作用又是如何实现对象的自救的呢首先我们要理解虚拟机在扫描到死亡对象的时候并不是直接回收的而是进行一次标记并且筛选筛选的条件就是其对象的finalize方法是否有必要执行。如果当前对象没有重写finalize方法或者已经调用过一次finalize方法那么则视为没有必要执行此时便失去自救的机会放入即将回收集合中。否则的话则将对象放入一个叫F-Queue的队列中稍后虚拟机将一个个的执行队列中对象的finalize方法(就是在此处对象可以在finalize方法中将自身关联到引用链从而暂时逃脱被回收的命运)需要注意的是虚拟机保证执行但不保证执行完finalize方法原因是如果finalize方法执行时间过长或者陷入死循环则可能让系统奔溃。全部执行之后虚拟机将对队列的对象重新标记一次如果还不在引用链中则GG否则将其移出即将回收集合。下面例子参考《深入理解JVM》实现自救并且验证只能自救一次的过程。public classTestForGc {/**定义一个根节点的静态变量*/public staticTestForGc INSTANCE;/*** 重写finalize方法让其被标记为有必要执行并且加入F-Q**throwsThrowable*/Overrideprotected void finalize() throwsThrowable {super.finalize();System.err.println(finalize method in TestForGc Class invoked!);//将自身关联到根节点中实现自救INSTANCE this;}public static void main(String[] args) throwsInterruptedException {INSTANCE newTestForGc();INSTANCE null;System.gc();//睡眠1S保证F-Q中的方法执行完毕TimeUnit.SECONDS.sleep(1);if(Objects.nonNull(INSTANCE)) {System.out.println(i successfully save myself by finalize method!);}else{System.out.println(i am dead :();}/** 下面验证finalize方法只能调用一次* 几乎完全一样的代码却是不同的结局*/INSTANCE null;System.gc();//睡眠1STimeUnit.SECONDS.sleep(1);if(Objects.nonNull(INSTANCE)) {System.out.println(i successfully save myself by finalize method again!);}else{System.out.println(couldnt invoke finalize again, i am dead :();}}}执行结果2 垃圾回收器如果说回收算法是接口那么垃圾回收器就是这些接口的实现类共有7种回收器接下来一一罗列。2.1 Serial垃圾回收器Serial是一种单线程垃圾回收器在工作的时候的时候会暂停所有的用户线程也就是stop-the-world虽然单线程代表了用户线程的停顿但是也意味着其不用进行线程的交互从而有更高的收集 效率。Serial采用复制算法是Client端新生代的默认垃圾回收器。其工作图类似于2.2 ParNew垃圾回收器。ParNew是Serial回收器的多线程版本是Server端新生代的默认回收器除了并行多线程之外其他包括实现都是一模一样当然也是采用复制算法。还有一点重要的是新生代的收集器除了Serial之外只有ParNew能跟年老代的CMS合作其在低CPU的情况下效率比Serial低但是在多个CPU的情况下要好的多。其工作图2.3 Parallel Scavenge垃圾回收器跟ParNew类似作用于新生代并行多线程并且也是采用复制算法。但是其关注的点却不同其着重的是一种叫做吞吐量的东西。所谓的吞吐量运行用户代码的时间 / (运行用户代码的时间 GC时间)也就是说其更加注重用户代码运行时间而不是减少GC停顿时间。相对于其他收集器来说可以更加高效的利用CPU更加适合作为在后台运算而不大需要交互的任务。Parallel收集器提供了两个比较重要的参数。-XX:MaxGCPauseMillis表示收集器将尽可能的在这个参数设定的毫秒数内完成回收工作。但这并不代表其设置的越低越好缩减回收时间是通过减少吞吐量换来的如果设置得太低可能导致频繁的GC。-XX:GCTimeRatio表示代码运行时间和垃圾回收时间的比率比如说设置为19那么则垃圾回收时间占比为 1 / (119) 5%默认是99。2.4 Serial Old垃圾回收器Serial的年老代版本同Serial基本相似不同的是采用的是标记-整理算法实现作为Client端默认的年老代收集器。如果在Server端的话那么其主要作用有二1、跟新生代的Parallel Scavenge收集器配合。2、做一个有价值的备胎当CMS垃圾回收器因为预留空间问题放不下对象而发生Concurrent Mode Fail时作为其备选方案执行垃圾回收。2.5 Parallel Old垃圾回收器Parallel Scavenge的年老代版本多线程并行同样注重吞吐量使用标记-整理算法。这个收集器可以跟新生代的Parallel Svavenge一起搭配使用在注重吞吐量和CPU资源敏感的场合中是一对很好的组合。2.6 CMS垃圾回收器来了它来了CMS垃圾回收器被当做是具有划时代意义的、真正实现并发的垃圾回收器总而言之》,--^----------,--------,-----,-------^--,| ||||||||| -------- | O---------------------------^----------|\_,-------, _______________________强__|/ XXXXXX /| // XXXXXX / \ // XXXXXX /\______(/ XXXXXX // XXXXXX /(________(------CMS是一款并发的垃圾回收器但并不代表全程都不需要停顿只是大部分时间是跟用户线程一起执行的。其整个GC过程中总共有4个阶段。1、初始标记简单的标记所有的根节点需要暂停所有的用户线程即stop-the-world耗时较短。关于GCRooots的过程可以看下另一篇文章——垃圾回收(一)。2、并发标记跟用户线程一起工作寻找堆中的死亡对象整个过程耗时最长。3、重新标记再次扫描主要对象是并发标记过程中又新增的对象也就是验漏。多线程需要STW时间相对并发标记来说短。4、并发清除GC线程跟用户线程一起执行清除标记的死亡对象浮动垃圾在此阶段产生。然而优秀如CMS也会有不足之处总共四个阶段的标记及清除算法的实现必定为其带来一些使用的麻烦。缺点1、占用一定CPU资源其有两个阶段需要并发跟用户线程一起执行也就是说要跟用户线程抢占CPU的时间片会占用一定的CPU资源如果CPU资源不太优质的情况下可能会造成不小的影响。2、空间利用率不能达到最大由于并发清除时用户线程也在运行那么在GC结束前必定会产生一些额外的垃圾那么就必须给这些垃圾预留一定的空间否则会导致内存不足从而报Concurrent Mode Failure此时虚拟机便启用后备方案——使用Serial Old来进行垃圾回收进而浪费更多的时间。3、内存碎片导致提前FullGCCMS采用的是标记-清除算法也就是说会产生内存碎片那么可能出现大对象放不下的情况进而不得不提前进行一次FullGC。为了解决这个问题虚拟机提供了两个参数-XX:UseCMSCompactAtFullCollection和-XX:CMSFullGCsBeforeCompaction分别表示CMS顶不住要进行FullGC的时候进行内存的整理(整理的过程中无法并发停顿时间不得不变长) 和进行多少次不压缩的FullGC之后来一次整理的GC(默认0次表示每次都进行内存整理)。2.7 G1垃圾回收器G1是一个新秀垃圾回收器被赋予了很大的使命——取代CMS。G1作为新时代的垃圾回收器相对于其他垃圾回收器来说有许多优势。1、并行和并发G1可以利用现在的硬件优势缩短GC时stop-the-world的停顿时间并且GC的时候同时也能让用户线程执行。2、分代收集跟其他垃圾回收器不同G1没有物理上的年老代和新生代其将内存分成了多个独立的Region每个Region都可能表示属于新生代还是年老代所以不需要一堆Region凑放在一起然后将这块区域称作新生代它们之间并不需要连续所以只有概念上的分代也是这种分代方式使得G1可以独立管理这个堆空间不需要跟其他回收器合作。3、空间整合G1的算法从Region层面看属于复制算法(从一个Region复制到另一个)但是从整体看又是标记-整理法。然而不管是哪种都表示G1不会产生内存碎片不会因为空间不连续放不下大对象而出现FullGC的情况。G1回收器将内存空间分成若干个Region并且这些Region之前相互独立。但是我们都知道这并不能真正的独立因为一个Region中的对象不一定只会被当前Region的其他对象引用而可能被堆中的其他对象引用那G1是如何实现避免全堆扫描的呢这个问题在分代的其他回收器中也有但是在这里突显得更加明显而已。再G1中对象本身都会有一个Remembered Set这个Set存放着当前对象被其他区域对象引用的信息这样子在扫描引用的时候加上这个Set就可以避免全堆扫描了。具体实现大致为虚拟机在发现程序正在进行对Reference类型的写操作时会暂时中断写操作然后检查Reference引用的对象是否处于不同的区域(如果是分代则只对年老代的对象进行检查检查是否引用的对象在新生代)如果是的话则将引用信息记录在被引用的Remembered Set中这样在GC的时候加上Remembered Set的扫描就可以避免全堆扫描了。跟CMS类型G1也有四个阶段(不算Remembered Set的扫描)虽然相似但是还是有些区别的。1、初始标记标记可达的根节点STW单线程时间短。2、并发标记跟用户线程同时执行并发执行时对象可能会产生引用变化其会将这些变化记录在Remembered Set Logs中待下个阶段整合。3、最终标记验漏将并发标记阶段的引用变化记录Remembered Set Logs整合到Remembered Set中。4、筛选回收对各个Region中的回收价值进行排序然后执行回收计划。暂停用户线程并行执行。3 小结本文首先介绍了“对象自救”的方法——finalize并且用一个小例子演示了对象如何实现自救。接着介绍了7种不同的垃圾回收器新生代中有单线程的Serial可以作为Client端新生代的默认回收器有多线程版本的Serial——ParNew还有着重点不同(吞吐量)的Parallel Scavenge年老代方面有单线程的Serial Old、跨时代意义的并发回收器——CMS虽然优秀但不可避免的有三个缺点、还有吞吐量年老代版本——Parallel Old收集器最后还简单介绍了G1收集器的几个过程还有独立的Region间是如何实现避免堆扫描的。整体下来整篇行文还有些粗糙日后会慢慢的圆润如果有关于这方面好的文章可以在下面评论区分享学习一下下方为各个垃圾回收器的搭配图。It helps me a lot if you could share your opinion with us.