其实已经有很多大牛在这方面做了很好的介绍,我在这篇文章里讲下我自己的一些理解,受限于我的认知水平,可能不一定正确,请自我甄别。
JVM的GC自动垃圾回收器是JAVA的一大特色,垃圾回收器要解决三大问题:where(去哪里回收垃圾)、what(对象是不是垃圾)、how(如何回收垃圾)。
问题一、WHERE
要了解垃圾回收必须了解JVM内存分配。
JVM像其他程序一样也是一个软件,用来处理class文件,就像文本阅读器处理文本。就像文本阅读器有很多种,处理class的也不止一种软件,只要符合JAVA虚拟机规范的都是JVM。例如:现在主流的是hotspot虚拟机,开源的有openJDK,IBM公司开发了自己J9。
JAVA虚拟机规范规定了JVM的运行时内存应该有这么几块:方法区、堆、JVM栈、本地方法栈、指令计数器(如图)
猜一猜 Object o = new Object() ;// Object类、 new Object()对象 、o 分别在哪个区域?
上图是逻辑划分,所有的虚拟机都必须有这么几部分,但是具体这几部分放在那里就是各自虚拟机的自我实现方式问题。下面这个图中是变量及对象的位置及引用:
Object类在方法区,new Object()对象在堆内, o变量引用在java栈
说了这么多,到底哪里有垃圾呢?
栈上的内存随着方法执行完成之后的出栈操作就把内存释放了,这里不需要担心垃圾。
方法区上的类信息也有可能不再使用而变成垃圾,但这个一般较少。
堆上创建的对象使用之后好像没有人专门去释放它,这里是主要的垃圾产生区!
问题二、What(对象是不是垃圾?)
计算机怎么知道一个对象不在被用了,主要有两种方法:
1、引用计数器法。每个对象记录下自己被引用的次数,为零不就没有被引用了。(呵呵,如果两个对象互相引用呢,这时可就回收不了了,内存泄露。)于是我们还有第二种方法。
2、根搜索算法(可达性分析法)。GcRoot根节点对象(就是确定了一定是在使用状态的对象,比如静态属性对象、当前栈等)引用到的对象也是在被GcRoot使用,一定不会被回收,他们引用的子对象也在引用链中(GcRoot根可达),除此之外的对象就都是垃圾。如下图object6、object7、object8三个对象因为不与GC Roots相连所以应该被回收。
问题三、How(如何回收垃圾)
回收的办法有很多,但是各种办法有各自的优缺点特性。为了JVM的空间利用、吞吐量、最大暂停时间等性能指标有不同种类的算法和策略。
算法有3种:
1、标记-清理算法。最简单的,先暂停线程运行,把所有的对象都遍历标记一遍,然后垃圾清理掉。这个就是标记-清除算法。如图:
这个缺点就是回收后的内存不连续,时间长了形成大量的“内存碎片”,如果有个大对象需要一块连续的大内存就可能分配不了。改进的算法是标记-整理算法。
2、标记-整理算法。一次遍历标记对象后,再一次遍历整个堆,将所有可达(存活)的对象都向一端移动(整理)。
这个好了内存连续了,但是也有缺点,多了一步移动对象的操作,效率低了。如果对象生命周期短还好(幸运的是一般情况下90%的对象生命周期都短),如果都是生命周期比较长的对象那么每次移来移去不是浪费时间吗。这两种应该是分别适用于生命周期长短的两种情况。还有一种清除效率更高的算法,复制算法.
3、复制算法:将可用内存按内容划分为大小相等的两块,每次只使用其中的一块。当第一块的内存用完了,就需要启动垃圾回收,将标记的可达对象复制到另外一个区块中,把已使用过的内存空间(第一个区块)一次清理掉。优点就是清理效率高,缺点就是浪费了一半内存,每次回收需要复制对象,适用于对象生命周期短存活率低的情况。
其实这三种算法有各自的优缺点和使用场景。为了提高效率,除了算法之外还有策略。
分代回收策略,根据统计90%以上的对象都是生命周期非常短的(如图,统计显示大部分的内存都是刚分配就被回收了,左侧突出部分),
根据这个特点就可以采取有针对性在单独的区域(新生代,老年代)里采取合适的算法进行回收:频繁回收的新生代区域和很少回收的老年代区域。其实还有个持久代,不过可以不考虑那个,那个基本不会回收。
虚拟机如何知道对象的生命周期长还是短呢?
jvm一开始也不知道,但是它会假设都是短的把它放到新生代区域,然后暗暗计算观察,每回收一次(新生代)就给对象的年龄加1,当对象经历多次回收(每次都年龄+1)都没有回收掉时它已经变成大年龄的对象了,可以预测后面也有较大可能不会被回收(一些应用基本的对象是伴随程序一直存在的),就可以把大年龄的对象移动到老年代区域去(老年代只在全局回收时才回收)。
如下图是一次局部次要回收(YGC:新生代回收),伊甸园(Eden,新生对象一般分配在这里,起这个名字大概因为外国圣经里最早的人是居住在伊甸园的)中的可达对象被复制到幸存区中(对象年龄+1),幸存区有两块交替使用。当幸存区的对象年龄达到年限时将被复制到老年代Old区。
http://47.98.203.123/?p=7