JVM跟Java无关,任何可以编译为.class文件的都可以在JVM上运行。
什么是垃圾
public static void main(String[] args) {
Object object = new Object();
object = null;
System.out.println(object);
}
上面的代码上,在object = null 之后,也就是当object这个变量指向另一块内存空间之后,原来的堆中的new Object()就没有变量指向它了,它就像一个没人要的孤独的孩子。此时它就是可以算为一个垃圾。
怎么找到垃圾
引用计数
没人任何引用指向的对象就是垃圾,当有一个引用指向时,就计数,有三个引用指向时,就计为3。当计数为0的时候,此时没有任何引用指向它,就是一个垃圾,可以回收。
这种方法会有循环引用的问题,如下图。这三个没有其它引用指向,这是三个垃圾。但在这种查找垃圾的算法下不会把这三个当作垃圾。
常见的垃圾回收算法
1.mark-sweep(标记清除)
哪些是垃圾就把那些标记了,然后清除。
优点:比较简单,容易理解。
缺点:可能会引起内存空间碎片化,即可能会找不到一块连续的完整的大空间来分配给一个大对象。
2.copying(拷贝)
只允许使用一半内存,每次垃圾回收时,把有用的拷贝到另一半连续的内存,然后把另一半内存空间直接清除。
优点:由于操作系统对一块连续的内存空间直接清除的速度非常快,因此效率比较高。
缺点:只能使用一半内存,浪费空间。
3.mark-compact(标记压缩、标记整理)
把垃圾标记然后清除后,会把有用的都移动到连续的内存中进行存储。
优点:不会引起内存空间碎片化。
缺点:效率比较低。
分代模型
把整个堆内存分为两节,分别是新生代和老年代。刚new出来的对象都在新生代,当对象每次经过一次垃圾回收存活后,年龄加一,当到达一定年龄的时候就会放到老年代。新生代跟老年代的空间空间大小大概是1:2。
新生代的特点:采用的是拷贝算法。新生代分为3个区域,eden区和两个survior区。伊甸区存放第一次new出来的对象,伊甸区的特点是每次回收会清除非常多的垃圾,每次只有大概5%-10%的对象能存活下来。存活下来的对象会放入第一个survior区,然后把伊甸区的空间清除。第二次回收垃圾的时候,eden区和第一个survior区都有存放东西,把eden区和第一个survior的非垃圾都放到另一个survior区后,清空eden区和第一个survior区。survior区总有一个是空的。为什么有两个survior区?存放年龄不足以是老年代的并且不是第一次new出来的对象。
老年代的特点:存放较多的对象,比较久时间间隔才进行一次垃圾回收,并且这些对象被回收的概率比较低。