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

459 阅读4分钟

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战」。

概述

当下因为内存泄漏、溢出排查以及当垃圾回收成为系统性能的瓶颈时,学习垃圾收集和内存分配策略就变的比较重要了。

Java的线程中的三块区域(虚拟机栈、程序计数器、本地方法栈)因为是随方法结束或者线程结束,内存会自动回收的,所以不需要过多考虑回收的问题。但是Java堆和方法区有较多的不确定性:一个接口的多个实现类所要的内存可能会不一样,而且只有在程序运行期间我们才能知道到底会创建多少个对象。因此我们需要垃圾收集机制来管理该部分内存。

对象是死是生?

垃圾回收之前需要判断哪些对象存活以及哪些对象已经死亡。

引用计数算法

引用计数法,就是在对象中加一个计数器,当对象被引用时候,该计数器就加一,如果不被引用后就减一,当该计数器数值为0时候就判断该对象已经死亡。引用计数算法是Python中主要的垃圾回收算法。

该算法原理简单而且效率也高,不过其有许多需要考虑的地方,需要配合大量的额外处理才能保证正确的工作。比如该算法很难解决循环引用的问题。所以在Python中垃圾回收机制以引用计数算法为主,标记-清除算法为辅,并以分代回收机制作为触发垃圾回收的条件。

可达性分析算法

该算法通过一系列称为GC Root的根对象作为起始点集,从这些节点开始根据关系向下搜索,搜索的路程称为引用链,如果某个对象到GC Root没有引用链相连则说明该对象是不可能再被使用的,也就是需要回收的对象。该算法应用在Java、C# 等语言的内存管理子系统中。

GC Root对象主要包括:

  • 虚拟机栈中引用的对象,比如说线程被调用的方法堆栈中用到的参数、局部变量和全局变量。

  • 方法区中类静态属性使用的对象

  • 方法区中常量引用的对象

  • 本地方法栈JNI引用的对象

  • Java虚拟机内部的引用

  • 所有被synchronized同步锁持有的对象

引用

引用主要有四个概念:强引用、软引用、弱引用以及虚引用

强引用:对象只要有该引用就不会被垃圾回收

软引用:被该引用的对象是对象虽然有用但是不是必须,所以其删除与否都可以,当内存空间不够的时候被软引用的对象会被删除

弱引用:被该引用的对象是一旦下一次垃圾回收开始,其必定会被删除。

虚引用:该引用知识一个概念,设置该引用主要是为了让该对象在被回收时候能够收到系统通知。

回收方法区

方法区垃圾回收的性价比比较低(JDK11版本的ZGC收集器就没有实现方法区回收),在Java堆中对新生代的回收通常一次可以回收70%-99%的内存空间,方法区主要回收废弃的常量不再使用的类型,废弃常量的回收比较容易实现,但是不再使用的类型的回收比较困难,正因为此所以方法区的垃圾回收的性价比不高

不再使用的类型的回收需要满足已下几个条件:

  1. 该类所有实例都被回收,也就是Java堆中不存在该类以及其派生子类的实例。
    
  2. 加载该类的类加载器已被回收。
    
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类方法。
    

结语

本文主要介绍了一些垃圾回收方面的知识和概念,算是为后面要写的介绍垃圾回收机制开了个头。今儿就到此结束,明儿继续写一些常见的垃圾回收机制。