为什么要有垃圾回收?
学过C/C++都知道每一次new出一个对象,在不使用时都要调用对应方法将其释放。程序每次开辟一块内存空间,在使用完毕后也就需要将其释放掉,而Java中为了防止程序员在开辟空间时忘记将其释放掉,保证程序员每次开辟空间在使用完毕后能将其释放,引入了GC垃圾回收机制,也就不需要程序员手动去释放。
垃圾回收主要作用的区域
在JVM中内存区域划分为:程序计数器,栈(Java虚拟机栈,本地方法栈),方法区,堆。
对于一个Java进程只有一份堆与方法区,而程序计数器与栈则每一个Java线程都有一份。
其中栈内存放的主要是局部变量,出了作用域也就销毁了,不需要GC。
堆中存放的主要是new出来的对象,也是GC的主要工作区域。
GC是如何确认垃圾的呢?
在JVM中的垃圾回收器主要使用的是可达性分析。
可达性分析:以代码中的一些特殊的变量为起点,看哪些对象能被访问到,标记为可达,剩下的不可达的也就是垃圾了。这里称为起点的变量(GCRoot),可以为栈内的局部变量,常量池中的对象,方法区中的静态引用类型的成员。
GC回收垃圾的算法。
垃圾回收的基础算法有:标记-删除,复制算法,标记整理,分代-回收。
标记删除可能会产生许许多多的内存碎片。
复制算法会造成内存空间的浪费。
标记整理虽然可以减少内存碎片的产生,提高内存利用率,但是比较费时间。
在JVM虚拟中GC使用的算法也就是分代-回收法。
这里首先为每个对象引入了一个新的概念,年龄:每经历一次GC年龄加1。根据年龄将对象分为新生代和老年代。新生区又分为:伊甸区和两个幸存区。
1.伊甸区中用于存放新参数的对象
2.伊甸区中的对象一般活不过一轮GC,活过第一轮GC的对象会通过复制算法进入幸存区。由于存活的对象少,所以复制开销可以接受。
3.进入幸存区的对象,也会继续接受GC,如果活过GC就会通过复制算法拷贝到另一份幸存区。这里幸存区的空间不必太大,不会浪费太多空间也不会产生内存碎片,还能保存复制算法的高效。
4.当对象在幸存区活过多轮GC,年龄达到了设定的值,就会被拷贝进老年区。
5.在老年代的对象也会经历GC但频率会降低,如果需要被销毁就会通过标记整理算法来处理,老年代的对象被回收的频率不高,所以标记整理带来的时间开销会不太搭。
例外:如果一个对象很大会直接进入老年代,因为大的对象在复制算法中会产生较大的开销。