一、概述我们在学习Java的时候都知道垃圾回收(GC),大多数人都把这项技术看作是Java语言的副产品。事实上,GC的历史比Java更久远。Lisp于1960年诞生于麻省理工学院,是第一个真正使用内存动态分配和垃圾收集技术的语言。那么今天就来研究一下垃圾回收的原理。2.对象死了吗?Java中的垃圾回收主要是堆内存的回收,堆内存存放了Java中几乎所有的对象实例。在进行垃圾回收之前,需要判断哪些还“存活”,哪些“死亡”。1.引用计数器方法为对象添加一个引用计数器。每当有对他的引用时,计数器值?1;正在使用。2、可达性分析算法以一系列称为“GCRoots”的对象为起点,从这些节点向下搜索。搜索所经过的路径称为“引用链”。当一个对象到达“GCRoots”而没有任何引用链连接时,证明该对象不可用。代码示例:GC日志如下:我们在GC日志中可以清楚的看到6092K->456K,这说明虚拟机并没有回收这两个对象,因为它们相互引用,所以Java虚拟机使用可达性分析算法标记。事实上,即使是可达性分析算法标记的不可达对象也不一定会被回收。虚拟机会对这些对象进行一次过滤,过滤的条件是是否需要对这个对象执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()已经被虚拟机调用时,虚拟机将这两种情况视为“不需要执行”。如果确定该对象被执行,finalize()方法与finalize()中的“GCRoots”相关联,则该对象不会被回收。3.垃圾回收算法我们知道虚拟机是如何标记一个对象是否可用的,那么它是如何回收的呢?其实堆内存是可以分为新生代和老年代的,新生代又分为一个Eden区和两个Survivor区,它们的比例是8:1:1,不同的垃圾收集器厂商给出了不同的算法这两个领域。1.新生代——复制算法新生代对象的特点是在一次GC中大部分对象都会被回收,所以采用了复制算法:新生代每次创建一个对象,只有一个Eden和一个他们将被使用。一块Survivor,垃圾回收时将存活的对象复制到另一块Survivor区,最后清理Eden和刚才的Survivor区。2.老年代——标记排序算法老年代一般保存一些大的对象,或者不经常回收的对象。根据特点,采用标记-排序算法:顾名思义,该算法分为“标记”和“排序”。”两个阶段:首先,标记所有需要回收的对象。标记完成后,收集所有标记的对象并排序,将所有标记的对象移动到一端,然后边界外的内存4.HotSpot算法实现上面提到了我们如何将对象标记为“死亡”以及如何进行垃圾回收,但是在HotSpot虚拟机中实现这些算法时,必须考虑算法的执行效率。1.可达性分析中安全点对执行时间的敏感度体现在GC停顿上,也就是说整个分析过程中看起来像是被冻结在了某个时间点,无法进行分析了引用关系在这个过程中是不断变化的,如果不保证这一点,就无法保证分析结果的准确性,这就导致需要暂停所有Java执行线程当GC正在进行时。当执行系统停止时,虚拟机不需要所有上下文和全局位置。虚拟机在加载类的时候通过一个OopMap数据结构记录了对象的偏移数据信息,所以GC扫描直接获取到这些信息。实际上,这些通过指令添加来记录对象信息的OopMap位置也被称为安全点。并不是所有的点都可以在程序执行时停止和启动GC,只有到达安全点才可以停止。安全点机制在程序执行过程中,在不太长的时间内会遇到一个可以进入GC的安全点。在实践中,会遇到GC时有些线程不再执行,比如线程被挂起。这就是我们需要一个安全区来解决的问题。2.安全区安全区是指在一段代码中,引用关系不会发生变化。在该区域的任何地方启动GC都是安全的。当代码执行到安全区的时候,首先意味着直接进入到安全区,这样虚拟机在这段时间的GC中是不关心标记为安全区的线程的。离开安全区时,首先要判断GC分析是否完成,未完成则需要等待。5.理解GC日志这是上面打印的GC日志[GC(System.gc())[PSYoungGen:6092K->448K(38400K)]6092K->456K(125952K),0.0051702secs][Times:user=0.01sys=0.00,real=0.00secs]其中,PSYoungGen表示新生代GC的新生代名称对于不同的垃圾收集器是不同的,6092K->448K(38400K)表示新生代大小的变化,6092K->456K(125952K)表示堆内存大小变化,其次是耗时。[FullGC(System.gc())[PSYoungGen:448K->0K(38400K)][ParOldGen:8K->378K(87552K)]456K->378K(125952K),[元空间:3050K->3050K(1056768K)],0.0056045secs][Times:user=0.00sys=0.00,real=0.01secs]这里的意思是老年代发生的GC(MajorGC/FullGC)只针对新代的GC(MinorGC)generation是伴随一次的,448K->0K(38400K)代表新生代内存变化,8K->378K(87552K)代表老年代GC变化,456K->378K(125952K)代表变化GC前后的堆内存。【本文为栏目组织《AiChinaTech》原创文章,微信公众号(id:tech-AI)》】点此查看作者更多好文
