垃圾回收机制
标记+清除
先标记哪些内存没有被引用,然后释放这些内存
注意,释放不代表要重写这些内存里的数据,只需要把这段内存的起始和结束的地址记录下来即可。
速度很快,但是很容易产生内存碎片
标记+整理
先标记哪些内存没有被引用,然后释放这些内存,
注意,释放内存之后要进行紧凑操作,也就是要把仍然有效的数据挨在一起,
不容易产生内存碎片,但是,花费时间较高,因为要牵扯到内存数据的复制还有仍然有效的这些对象地址的修改
标记+复制
from to
0 1 1 1 0 0 0 0 0 0
我们对from区的数据进行标记,把仍然有效的数据都复制到to区
from to
0 0 0 0 0 1 1 1 0 0
最后,交换from和to区域的指针,把这两块区域换过来
保持to区一直是空闲的。
from to
1 1 1 0 0 0 0 0 0 0
不会产生内存碎片,但是需要占用两倍的内存空间
分代垃圾回收
将内存区域分为新生代和老年代,
新生代的区域存放那些使用过后就被回收的对象,
而老年代则存放那些需要一直保存的对象,
新生代 老年代
伊甸园 from to
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
新产生的对象分配在伊甸园区域,
当伊甸园区域内存不够时,进行minorgc垃圾回收,
把仍然有效的对象放入to区,给这些对象的寿命+1,然后交换from和to,保持to一直是空的状态,
minorgc之后,伊甸园腾出空间,我们可以继续往里面存放对象,当伊甸园空间又不足的时候,我们再次进行minorgc,这一次不仅要考虑伊甸园中的对象是否存活,还要考虑from中的对象是否存活,把存活的仍然存活的对象放入to区,对其寿命+1,
当经过多次回收的对象仍然存活时,也就是from区的对象寿命超过一定值的时候(默认15,因为使用4bit存储这个寿命),我们考虑将其放入老年代,
当老年代的内存也满了的时候,先进行一次minor gc如果内存还不足,再进行fullgc,对新生代和老年代进行统一的清理。
minor gc和full gc 会引发stop the world,也就是会停下用户线程,只允许垃圾回收的线程运行,因为垃圾回收涉及到了对象的移动操作,如果不停下用户线程,用户线程可能会去访问一个已经被清空的地址,产生混乱。等垃圾回收结束,用户线程再次运行。minor gc这个暂停时间是很短的,毕竟大多数的新生代都是垃圾,不需要移动很多。full gc暂停时间较长。
如果我们需要存放一个大对象,伊甸园没有这么大的空间而老年代却可以放得下时,会直接放入老年代。
如果在一个线程中发生了内存溢出,并不会影响到主线程main的运行。
垃圾回收器
串行
单线程,堆内存较小,适合个人电脑
吞吐量优先
多线程,堆内存较大,多核cpu,在一定时间范围内让STW最短
响应时间优先
多线程,堆内存较大,多核CPU,只关注单次STW,让每次STW最短