Java垃圾回收是指 堆区的垃圾回收,栈区的数据都是有自己的生命周期的,由操作系统负责回收。
每隔一段时间会进行一次GC。
JVM把堆区划分为三个部分:新生代,老年代和永久代。
1、永久代保存类的元数据信息和方法描述,一般不变,垃圾回收一般针对新生代和老年代。
2、不管是新生代的survivor0,survivor1还是老年代,都有标记清除算法。
堆区数据都有一个引用计数(类似于智能指针),引用赋值会使计数加1,引用生命周期结束(出了作用域)会使引用计数减1。当引用计数减为0,就认为这个数据是垃圾数据,会把他回收掉。但是这样会产生很多内存碎片。不为0的就是存活数据。
3、新生代划分为Eden区和survivor区,采用复制算法进行GC。
Eden(伊甸园),用来保存新出生的对象,刚new出来的对象都保存在Eden区,GC会把Eden里面的存活数据复制到Survivor区,把Eden清空。当survivor满了就放到老年代。
4、由于survivor区也有标记清除算法,会产生大量的内存碎片,导致survivor区很快就满了。所以把survivor分为survivor0和survivor1 。每次GC先把Eden里面的存活数据和survivor0的存活数据复制到survivor1,清空Eden和survivor0。再把survivor1的数据复制到survivor0,清空survivor1。这样survivor0里面的数据就都是连续的了,减少了内存碎片。
5、如果新生代里面的数据一直都有引用,15次GC之后,还存活。就会把他放到老年代。
6、老年代使用标记整理算法进行垃圾回收。
由于刚new出来的对象一般生命周期比较短,每次GC,Eden存活的 数据比较少,所以采用复制算法耗时短,复制占用的survivor区也少,不浪费内存。
但是老年代的数据一般生命周期是很长的(在新生代已经经历过了15次GC仍然存活)。因此老年代里面的采用复制算法会很耗时,也会很费内存。一般使用标记整理算法,把引用计数为0 的内存释放掉,产生了一些内存碎片。在移动内存的地址,将其变成连续的地址。
7、老年代空间如果满了,会触发一次FullGC(如果老年代没满只会对新生代进行GC)。FullGC很耗时间。
java内存泄漏
1、静态对象的生命周期与程序周期一致,如果静态类持有其他对象的引用,引用计数一直不为0,持有的对象不能够被回收
2、连接不关闭,连接对象的引用一直不为0,内存泄漏
3、