本文简单介绍一下垃圾回收的概念及算法
一、垃圾回收算法
二、垃圾回收停顿现象
三、对象如何进入老年代
四、TLAB区域
一、垃圾回收算法
垃圾回收(Garbage collection,简称GC),类比生活中的垃圾,会把垃圾丢入垃圾桶中,然后清理掉。GC中的垃圾特指存入内存中、不会再被使用的对象,而回收相当于将垃圾倒掉。
垃圾回收算法有很多种:
①引用计数算法
②标记压缩算法
③复制算法
④分代、分区思想
1、引用计数法
比较古老经典的算法,其核心是在对象被其他所引用时计数器加1,而引用失效时则减1。但是这种方式有非常严重的问题:无法处理循环引用的情况,每次进行计数的加减操作比较浪费性能。
2、标记清除算法
分为标记和清除两个阶段对内存中的对象进行处理。
该种方式的弊端:空间碎片问题,垃圾回收后空间是不连续的,不连续的内存空间工作效率要低于连续的内存空间。
3、复制算法(堆中年轻代垃圾回收)
核心思想将内存空间分为两块,每次只使用其中的一块,在垃圾回收时,将正在使用的内存中的存留对象复制到未被使用的内存块中,之后清除之前正在使用的内存块中所有对象,反复交换两个内存的角色,完成垃圾收集。(java新生代中from,to空间使用这种算法)
4、标记压缩算法
标记压缩算法是在标记清除算法的基础上做了优化,把存活的对象压缩到内存的一端,而后进行垃圾清理。(堆中老年代使用标记压缩算法)
问题:为什么新生代与老年代使用不同的算法?
新生代对象生命周期比较短需要频繁进行GC,而老年代对象声明周期较长,GC次数多会影响性能。
5、分代算法
就是根据对象的特点将内存分为N块,而后根据每个内存的特点使用不同的算法。
对于新生代与老年代来说,新生代回收的频率比较高,而每次回收耗时较短,而老年代回收频率较低,但是耗时会相对较长,所以应该尽量减少老年代的GC。
6、分区算法
其主要将内存分为N多个小的内存空间,每个小空间都可以独立使用,这样可以细粒度的控制回收的内存空间,而不需要对整个空间进行GC,从而提升性能,并减少GC的停顿时间。(可以类比Hashtable与ConcurrentHashMap)。
二、垃圾回收停顿现象
垃圾回收器的任务是识别和回收垃圾对象进行内存清理。为了让垃圾回收器高效执行,大部分情况下,会要求系统进入一个停顿的状态。停顿的目的是终止所有的应用线程。这样系统才不会有新的垃圾产生,同时停顿保证了系统状态在某一瞬间的一致性,便于更好的标记垃圾对象。因此,在垃圾回收时,都会产生应用程序的停顿。
三、对象如何进入老年代
一般而言,对象首次创建都会被放置在新生代的eden区,如果没有GC的介入,该对象会一直存在eden区。
对象如何进入老年代?
如果对象达到一定的年龄大小,就会自动离开年轻代进入老年代。对象的年龄由经受GC的次数决定,在新生代每次GC之后如果对象没有被回收则年龄加1。
虚拟机提供一个参数来控制新生代对象的最大年龄,当超过这个年龄则会晋升到老年代。
-XX:MaxTenuringThreshold,默认情况下为15。
根据-XX:MaxTenuringThreshold参数,可以指定新生代对象经过多少次回收进入老年代。
另外,对于大对象(新生成代eden区无法直接载入时,直接进入老年代)。JVM中有参数可以设置对象的大小超过指定的大小之后直接进入老年代。
-XX:PretenureSizeThreshold 参数设置对象大小超过改参数后进入老年代。
注意:-XX:PretenureSizeThreshold可以指定进入老年代对象的大小,但是要注意TLAB区域优先分配空间。
四、TLAB区域
TLAB全称为(Thread Local Allocation Buffer )即线程本地分配缓存,线程专用的内存分配区域,为了加速对象的分配而产生的。
每一个线程都会有一个TLAB,该线程独享的工作区域,java使用这种TLAB区域来避免多线程冲突的问题,提高了对象分配的速率。TLAB空间不会太大,当大对象无法在TLAB分配时,则直接分配到堆上。
-XX:+UseTLAB 使用TLAB
-XX:+TLABSize 设置TLAB的大小
-XX:TLABRefillWasteFraction 设置维护进入TLAB空间的单个对象的大小。他是一个比例值,默认为64,如果对象大于真个空间的1/64,则在堆中创建对象。
-XX:+PrintTLAB 查看TLAB信息
-XX:ResizeTLAB 自动调整TLABRefillWasteFraction的阈值。