本文是学习《深入理解JAVA虚拟机》的学习笔记(2)此篇是概念性东西,可结合下节的垃圾回收器一起看
Java是可以自动回收内存的,什么时候需要虚拟机调优呢?当垃圾回收成为系统达到更高并发的瓶颈时。
垃圾回收第一步要确认哪些对象可以回收。就是那些不能再被任何途径使用到的对象。怎么找到这些对象,这就谈到可达性分析法。引用计数法,由于循环引用的问题,主流虚拟机均没选用它,这里就不再论述。
可达性分析(Reachability Analysis)
基本算法思想是通过一系列的GC Roots 开始搜索,所走过的路径就是引用链(Reference Chain),当某个对象没有任何引用链连接,即认为此对象不可用,就是被回收的对象。
那啥是GC Roots呢,在JAVA语言中包括:
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象
回收方法区
方法区中 废弃常量与无用类也可回收。其中判定无用类的条件很苛刻,有兴趣的可自己研究。
垃圾回收算法
标记-清除算法(Mark-Sweep)
最基础的算法,其后的算法是在此思路上有针对的改进。简单说,就是先标志出所有需要回收的对象,再一次性回收所有被标记的对象。
不足有两个:一是效率不高,二是产生大量不连续的内存碎片。
复制算法(Copying)
解决回收效率问题。把可用内存一分为二,每次只使用其中一块,当这块内在满了,把它上面还存活的对象,复制到另一块。然后再把原来已使用过的那块内在空间一次性清理掉。
优点:可以不用考虑内在碎片问题,复制时只要移动堆顶指针,按顺序分配内在就行了,简单高效。
缺点:是只使用一半的内存,浪费。
现在商用虚拟机采用这种算法时,改进了,不是按1:1来分。而是将内存分为一块较大的Eden空间和两块较小的Survivor空间。每次使用Eden和其中一块Survivor,回收时,先将这两块中存活对象复制到另一块Survivor上,然后一次性清理这两块。HotSpot中默认Eden和两个Survivor的比例是 8:1:1。内存利用率就从50%提高到90%。
这里涉及一个问题,回收时,Eden和Survivor上存活对象,在另个Survivor上放不下时,会引出分配担保(Handle Promotion)这个问题,随后再细说。不用担心放不下,人家可以借用别的地方的内存
标记整理算法(Mark-Compact)
与“标记-清除”算法一样,也是先进行标记,之后与其不同,是让所有存活的对象向一端移动,然后直接清理掉边界以外的内存。
分代收集算法(Generational Collection)
根据对象存活周期不同而将内存划分为几块,一般把JAVA堆分为新生代和老年代,根据各个代不同的特点,采用适当的收集算法。在新生代中,垃圾收集时都会发现有大批对象死去,只有少量存活,那就选用复制算法,将存活的对象复制就可以完成回收。而老年代对象存活率高、没有额外的空间对它时行分配担保,必须使用“标记-清除”或“标记-整理”算法来进行回收。相关详细内容,之后系列文章会详细介绍