原创

JVM学习笔记——JAVA垃圾回收机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lyhkmm/article/details/79571441

垃圾回收机制的意义

  Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存。

垃圾回收机制中的算法

  Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:(1)发现无用信息对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用。

空间分布和垃圾回收流程

这里写图片描述
  由上图所示,Java的堆空间被分为了三个区域,分别是新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)。
  在老年代中,存放着从新生代中经历了多次垃圾回收后仍然存活的对象,这些对象相对较少,而老年代内存一般很大,并不容易塞满,因此老年代的垃圾回收频率要远远低于新生代,从而减少了对系统性能的影响。永久代中主要存放Java类本身的数据信息,当Java类不再被使用时,也会被垃圾回收掉。
  开发者们通常无法预测永久代的大小,导致程序经常出现“java.lang.OutOfMemoryError: Permgen space”错误,因此在Java 8中,使用jvm进程原生内存空间的Metaspace代替了永久代。在默认情况下,Metaspace将使用jvm进程所有可用的内存。

垃圾回收算法

  标记清除算法 (Mark-sweep )
  将需要清除的对象标记,然后直接清除,这样就会留下很多分散的存储空间,如果有大对象插入,会存在效率问题与空间问题(产生大量不连续的内存碎片)。如下图所示:
  这里写图片描述
  复制算法(Copying)
  应用十分广泛,将内存分为一块较大的Eden和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。回收时,将Eden与Survivor复制到另一块Survivor是哪个。
默认Eden与Survivor的比例是8:1。如下图所示:
  这里写图片描述
  标记整理算法(Mark-Compact)
  适用于老年代,将所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。如下图所示:
  这里写图片描述
  Generational Collection(分代收集)算法
  分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
  目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间,两个Survivor空间互相交换工作。而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。
  注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。

垃圾收集器

  在Java中主要有4种垃圾收集器,他们各自对于不同的内存代采用不同的算法。Java会根据当前系统的基本配置确定一个默认的垃圾收集器,你可以通过以下命令查看:
  串行收集器(Serial Collector)
  顾名思义,串行收集器指采用单线程进行垃圾回收,回收时会导致长时间的Stop-The-World,主要用于单机程序。该收集器在新生代采用复制算法,在老年代采用标记-压缩算法。可以通过-XX:+UseSerialGC命令行选项激活该收集器。
  并行收集器(Parallel Collector)
  该收集器同样在新生代采用复制算法,在老年代采用标记-压缩算法,只是使用了多线程的方式进行垃圾回收,从而大大提高了回收效率,但是回收过程中同时需要Stop-The-World。可以通过-XX:+UseParallelGC激活该收集器。多数情况下,并行收集器是Java的默认收集器。
  并发标记清除收集器(Concurrent Mark Sweep Collector,CMS)
  该收集器在在新生代中采用复制算法,在老年代采用标记-清除算法(不是标记-压缩)。之所以叫“并发”,是因为在回收过程的某些阶段,回收线程和用户线程同时执行,当然不是整个回收过程都可以和用户线程并行的,该收集器也存在Stop-The-World的时候,只是相比于其他收集器来说Stop-The-World持续时间较少而已。可以通过-XX:+UseConcMarkSweepGC激活该收集器。
  G1收集器(Garbage First Collector)
  G1收集器是Java世界最新的收集器,在Java 9中,它将成为默认的垃圾收集器。该收集器采用与上文中提到的收集器不同方式来对待Java对内存,如下图所示。可以通过-XX:+UseG1GC激活该收集器。

名词解释

  Stop The World
  从GC Roots进行可达性分析是,为了保持一致性,停顿所有的Java执行线程。就像时间冻结。
  准确式GC
  虚拟机可以知道内存中某个位置的数据具体是什么类型
OopMaps:用来记录对象引用,HotSpot使用,类加载过程中,把对象内的偏移量上是什么数据计算出来。
  安全点
  SafePoint 既程序执行时并非在所有地方都能停顿下来,只有安全点才能停顿。程序长时间执行的特征,例如:方法调用、循环跳转、异常跳转等
  抢先式中断和主动式中断
  抢先式首先把所有的线程全部中断,如果发现有线程不在安全点上,则回复线程;主动式中断:通过设置标志,线程执行时轮询标志,发现标志则自己中断。
  安全区域(Safe Region)
  如果程序不执行的时候,就没法中断,所以需要安全区域,一段代码片段中,应用关系不发生变化,则为安全区域。

原文来自:lyhkmm.com

正文到此结束
Loading...