我爱编程

JVM:垃圾收集器与内存分配策略(上)

2018-06-21  本文已影响0人  renyjenny

概述

要研究的问题

为什么要了解GC和内存分配:需要排查各种内存溢出、内存泄露的问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,就需要对这些“自动化”的技术实施必要的监控和调节。
需要回收的区域:程序计数器、虚拟机栈、本地方法栈三个区域是线程私有的,其生命周期和拥有它们的线程的生命周期一致,其内存分配和回收都具有确定性,所以不需要进行讨论。Java堆和方法区这两个区域,要在程序运行时才能确定下来,其内存分配和回收都是动态的,是垃圾回收的关注区域。

对象存活判断

不会再被使用的对象就是不再存活的对象,其存活的判断有以下两种算法。

引用计数算法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器加1,当它失去一个引用时,计数器减1。当计数器为0时,则表示该对象已死。
缺点:无法解决对象间循环互相引用的问题。


引用计数算法.png

途中对象objA和objB现在分别被引用,其中objA中的instance字段引用着objB,objB也是如此。按照引用计数算法,它们的计数器都为2。此时令objA=null,objB=null,它们的计数器分别为1。事实上这两个对象都不会再被使用了,但因为它们彼此引用着,将永远不会被回收。

可达性分析算法

以“GC Roots”对象作为起点往下搜索,搜索走过的路径称为引用链(Reference Chain)。如果一个对象没有与GC Roots没有引用链相连,那这个对象是不可用的,将会被回收。
可以称为GC Roots的对象:

引用分类

回收经过

对象回收.png

对象的Finalize()方法只会被系统自动调用一次(对象只会有一次自救的机会)。
不建议在finalize()方法中自救,事实上,在JDK9中finalize方法已经被标记为deprecated。

方法区回收

回收内容:废弃常量和无用的类。
常量废弃判断:与对象存活判断相似,当常量没有被引用后就可以废弃。
类无用判断:

垃圾收集方法

标记-清理算法

如回收经过过,对象经过两次标记后再回收。首先将需要回收的对象标记,再一次清理。
缺点:

复制算法

将可用内存按容量划分为大小相同的两块,每次只在一块上分配内存,另一块保留。可分配块的内存用完了,就将存活的对象复制到保留区,再清空可分配区,之后,可分配区与保留区对换。
优点:实现简单、运行高效,不用再考虑内存碎片的问题,分配内存只需移动内存指针。
缺点:每次只能使用一半内存,性能不高。


复制算法.png

目前的商业虚拟机均采取此种方式进行垃圾回收,但保留区与分配区并不是1:1划分的。将内存分为一块较大的Eden空间和两块较小的Survivor空间,比例为8:1:1。因为新生代中的对象,98%属于“朝生夕死”的,所以清理后还剩下的对象,移到其中一块Survivor上已经足够了。新生代中的可用内存空间为整个新生代容量的90%,只有10%作为保留区。

标记-整理算法

标记-清理算法的升级版,标记后将存活的内存移到内存的一端去,再将其他的清理掉。
优点:避免了内存碎片的产生。
缺点:效率不高。


标记-整理.png

分代收集算法

根据对象存活周期的不同,将内存划分为几块,如新生代和老年代。新生代中的对象存活率低,每次收集后仅有少部分存活,可以使用复制算法。老年代中对象存活率高,且没有额外空间对它进行分配担保,所以使用标记-清理或标记-整理算法。

上一篇 下一篇

猜你喜欢

热点阅读