JVM GC垃圾回收机制简述

  Java相比于C++一大特点便是其特有的自动垃圾回收机制,有了它,我们就可以不用再关系内存分配的问题了,也一般不需要担心出现内存泄漏。大家也可以愉快的聚焦于业务发展。那么Java的GC回收到底是怎么实现的呢,本文就来简单的说一下。

要想了解Java的GC机制,首先要对JVM的内存结构有一定的了解,首先让我们来一起了解一下。

1、JVM内存结构
JVM内存结构通常来说由虚拟机栈、堆、本地方法栈、方法区、程序计数器组成,其中每个组件各司其职。下面我来一一简单介绍。

 

1.1方法区(Method Area)
方法区中主要存储了Java代码中的类加载的相关信息,比如类的名称、修饰符、构造器、静态方法、定义为final的常量、勒种的方法信息。这是一个面向全局线程共享的区域。

1.2虚拟机栈(VM Stack)
这一区域主要存放了大量的线程相关信息,它是随着虚拟机线程的诞生而诞生的,所以它是独属于线程私有的一块区域,其中存放了Java八大基本类型的变量,还有一些局部变量,也就是方法内部的变量。

1.3本地方法栈(Method Area)
这里存放了Java代码中调用的本地方法的信息,主要用于native方法。

1.4堆(Heap)
堆是Jvm立马很重要的一个区域,也是跟方法区一样属于全部线程都可以共享访问的一个区域,在堆中存储了大量的Java对象信息,但凡是new出来的对象都存放在这里,并且可以通过jvm提供的-Xmx和-Xms来调节堆的大小。其中-Xmx是堆的最大值是多少,-Xms是堆的最小内存,如果两者一致,则这个Jvm的堆大小就不能弹性伸缩。 

 

 

事实上,堆内部也划分除了多个区域,分别是新生代、老年代以及持久代。这其中新生代又分为Eden和Survivor01、Survivor02等等。为什么要这么划分,这就涉及到了Jvm的GC回收策略了。

新生代中存储了所有新创建的对象,一个新对象创建OK首先是存放在Eden区域的,当Eden区域快要被填满时,就会自动触发Jvm的GC机制,GC会回收一些空闲的对象,再将幸存下来的其他对象转移到Survivor01中去,同样,如果01也满了 再次对01这一区域进行GC回收,将幸存者放入Survivor02区域中去。一旦在Survivor02中历经磨难多次存活超过15次,就会将其转移至老年代中去。至于持久代中,则主要存储一些常量池、方法区等数据。相关参数如下:

-Xmx:最大堆内存,如:-Xmx512m
-Xms:初始时堆内存,如:-Xms256m
-XX:MaxNewSize:最大年轻区内存
-XX:NewSize:初始时年轻区内存.通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
-XX:MaxPermSize:最大持久带内存
-XX:PermSize:初始时持久带内存
-XX:+PrintGCDetails。打印 GC 信息
-XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3

-XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10

 

2、Jvm GC 回收策略
JvmGC经过这么长时间的发展,逐渐划分了以下几个垃圾回收策略,他们分别是:

2.1、复制回收算法
    此种方法通过依次扫描区域所有的可达对象,然后将其复制到另外一片区域保存起来,再将其现在正在使用的区域内存全部清空,此方法的优点在于方便快捷,只需要便利出所有的可达对象即可,而且不会出现碎片化内存。但是缺点也很明显,复制对象需要计算成本,此外需要准备一个额外相同Eden区域大小的内存空间,也是一笔巨大的开销。

2.2、标记清除法
    这种方法首先遍历整个区域中的对象,然后标记所有的可达对象,再将所有内存中未被标记的对象全部清除。主要缺点在于会产生大量的碎片内存。

2.3、标记整理法
    这种方法集上面两种算法的优点于一身,首先遍历整个空间对可达对象进行标记,然后再讲所有可达对象整理到一起去,最后清除掉不可达的对象,达到GC回收清理内存的目的。

3、Jvm如何检测对象是否有用?
    上面一个章节我们简要的叙述了Jvm GC的回收策略,但是大家一定很好奇,Jvm是如何判断一个对象是否已经无人再使用的,这里大致说一下Jvm的策略。

3.1、引用计数法
    顾名思义,这种方法其实很简单,就是你new出来一个对象之后,之后每次对该对象做了引用,那么就将该对象+1,在GC时,只需要判断该对象对应的count是否为0就可以轻松判断出是否还存在引用关系。

    但是这种方法很不严谨,循环引用会使得其产生内存泄漏,永远无法释放这些资源。

3.2、根搜索算法
    这个算法其实也很简单,如下图所示,Jvm会起一个后台守护进程来维护一个树结构,如果发生引用就在树上维护一条边,那么同样的,如果这个引用被释放了,那么这个类也就和这个树失去了链接,从根节点GC Root对其进行搜索也搜索不到,便可以判断其对象已经无人再引用,可以释放。

 

posted @ 2021-07-07 10:37  Materben  阅读(218)  评论(0编辑  收藏  举报