垃圾回收概念和其算法
垃圾回收(Garbage Collection 简称GC) GC中的垃圾,特指存在内存中,不会再被使用的对象,而回收就是相当于把垃圾“倒掉”。垃圾回收有很多种算法:如引用计数法、标记压缩法、复制算法、分代、分区的思想
引用计数法:古老而经典的垃圾收集算法,核心就是在对象被其他所引用时计数器加1,而当引用失效时则减1,但是会存在一个严重的问题:无法处理 循环引用的情况,而且每次进行加减操作会浪费系统的性能。
标记清除法:就是分为标记和清除两个阶段进行处理内存中的对象,当然这种方式也是有非常大的弊端,就是空间碎片问题,垃圾回收后的空间不是连续 的,不连续的内存空间工作效率要低于连续的内存空间
复制算法:其核心思想就是将内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存留对象赋值到未被使用的内存块中,之 后去清除之前正在使用的内存块中所有对象,反复去交换连个内存的角色,完成垃圾收集。(java中新生代的from和to就是使用这个算法)
标记压缩法:标记压缩法在标记清除法的基础上做了优化,把存活的对象压缩到内存一端,而后进行垃圾清理。(java中老年代使用的就是标记压缩法)
考虑:为什么新生代和老年代使用不同的算法
新生代对象死的快
分代算法:就是根据对象的特点把内存分成N块,而后根据每个内存的特点使用不同的算法。对于新生代和老年代来说,新生代回收频率很高,但是每 次回收耗时都很短,而老年代回收频率较低,但是耗时会相对较长,所以应该尽量减少老年代的GC。或者说其主要就是将整个内存分为N多个小的独立空间,每个小空间都是可以独立的,这样细粒度的控制一次回收都是单个小空间,而不是对整个 空间进行GC,从而提升性能,并减少GC的停顿时间。
分区算法 oracle收购后尝试的
垃圾回收时的停顿现象
垃圾回收器的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器可以高效的执行,大部分情况下,会要求系统进入一个停顿的状态。停顿的目的时终止所有应用线程,只有这样系统才不会有新的垃圾产生,同时停顿保证了系统状态在某一个瞬间的一致性,也有益于更好的标记垃圾对象。因此在垃圾回收时,都会产生应用程序的停顿
对象如何进入到老年代
一般而言对象首次创建会被放置在新生代的eden区,如果没有GC介入,则对象不会离开eden区,那么eden区的对象如何进入老年代呢?一般来讲,只要对象的年龄达到一定的大小,就会制动离开年轻代进入老年代,对象年龄是由对象经历数次GC决定的,在新生代每次GC之后如果对象没有被回收则年龄加1.虚拟机提供一个参数来控制新生代对象的最大年龄,当超过这个年龄范围就会晋升老年代。
-XX:MaxTenuringThreshold 默认情况下为15.
总结:根据设置MaxTenuringThreshold参数,可以指定新生代对象经过多少次回收后进入老年代。
另外,大对象(新生代eden区无法装入时,也会直接进入老年代)。JVM里有个参数可以设置对象的大小超过指定的大小之后,直接晋升老年代
-XX:PretenureSizeThreshold
总结:使用PretenureSizeThreshold可以进行指定进入老年代的对象大小,但是要注意TLAB区域优先分配空间。