JVM垃圾回收重要理论剖析【纯理论】

JVM学习到这里,终于到学习最兴奋的地方了---垃圾回收,在学习它以前还得对JVM垃圾回收相关理论知识进行了解,而后再经过实践来加深对理论的理解,下面直接开始了解相关的理论:html

JVM运行时内存数据区域:java

这个在以前其实已经介绍过了,对于JVM的垃圾回收必定是回收内存里面的内容,因此若是不对内存区域的划分,区域存放的内容有所了解,那何谈垃圾回收呢?因此看一下下图对内存区域的划分描述:web

其这以上区域在上一次【http://www.javashuo.com/article/p-xzdzxdar-do.html】都已经讲过了,下面再来总体回顾一下。算法

方法区域:框架

其中它是数据是线程共享的:学习

那所谓方法区域是线程共享的是指的啥意思呢?好比说一个类的class元信息就会映射到方法区域当中,那么这个元信息会被全部的线程所访问,由于只有一份,因此该区域是线程共享的。spa

下面再来阐述一下它:线程

  • 存放了每一个Class的结构信息,包括常量池、字段描述、方法描述。
  • GC的非主要工做区域。

Java虚拟机栈(JVM Stack):3d

下面再对其进行一些阐述:代理

  • Java虚拟机描述的是Java方法的执行模型:每一个方法执行的时候都会建立一个帧(Frame),栈用于存放局部变量表,操做栈,动态连接,方法出口等信息。一个方法的执行过程,就是这个方法对于栈帧的入栈出栈过程。
  • 它是线程隔离的,如图上所示。

本地方法栈:

 

堆:

 它里面的数据也是线程共享的:

下面再来阐述一下它:

  • 堆里存放的是对象的实例。
  • 是Java虚拟机管理内存中最大的一块。
  • GC主要的工做区域,为了高效的GC,会把堆细分更多的子区域。【这个在以后会细说】
  • 它是线程共享的,如图所示。

程序计数器: 

JVM运行时数据区域例子:

对于这样一个方法代码:

以上方法在执行以后在内存中发生的变化以下:

  • 生成了2部分的内存区域:一、obj这个引用变量,由于是方法内的变量,放到JVM Stack里面;二、真正Object class的实例对象,放到Heap里面。
  • 上述的new语句一共消耗12个bytes,JVM规定引用占4个bytes(在JVM Stack),而空对象是8个bytes(在Heap)。
  • 方法结束后,对应Stack中的变量立刻回收,可是Heap中的对象要等到GC来回收。

JVM垃圾回收(GC)模型:

垃圾判断算法:

  • 引用计数算法(Reference Counting)
    ①、给对象添加一个引用计数器,当有一个地方引用它,计数器加1,当引用失效,计数器减1,任什么时候刻计数器为0的对象就是不可能再被使用的,比较好理解。
    ②、引用计数算法没法解决对象循环引用的问题。啥意思,下面用图来讲明一下:

    JVM中存在A、B两个对象,而A、B是相互引用着的,也就是A里面持有B的引用,而B里面又持有A的引用,以下:

    而开始这两对象是被其它地方所引用着的,好比方法栈中,以下:


    而以后虚拟机栈的这两个引用消失了,也就是整个虚拟机中就只有这两个相互引用的对象了,而这两对象不被任何对象所引用着了:

    而根据引用计数器的定义规则,A和B的引用计数器都是1,可是实际这俩都是孤立的对象,因此若是采用引用计数来进行垃圾回收,则这俩对象永远不会被回收。

  • 根搜索算法(GC Roots Tracing) 
    既然引用计数算法存在对象循环引用的问题,因此此算法出现了,下面具体看下该算法:
    ①、在实际的生产语言中(Java、C#等),都是使用根搜索算法判断对象是否存活。
    ②、算法基本思路就是经过一系列的称为“GC Roots”的点做为超始进行向下搜索,当一个对象到GC Roots没有任何引用链(Reference Chain)相连,则证实此对象是不可用的。 
    ③、在Java语言中,GC Roots包括【也就是什么是GC Roots?】:
         a、在vm栈(帧中的本地变量)中的引用。
              因此根据根搜索算法,因为目前没有栈中的引用指向A和B对象,因此这个算法规则这俩对象是能够被回收的了,以下:
              
         b、方法区中的静态引用。
         c、JNI(既通常说的Native方法)中的引用。

方法区:

  • Java虚拟机规范表示能够不要求虚拟机在这区域实现GC,这区域GC的“性价比”通常比较低。
  • 在堆中,尤为是在新生代,常规应用进行一次GC通常能够回收70%~80%的空间,而方法区的GC效率远小于此。
  • 当前的商业JVM都有实现方法区的GC,主要是回收两部份内容:废弃常量与无用类。
  • 类加收须要知足以下3个条件【条件是极其苛刻的】:
    一、该类全部的实例都已经被GC,也就是JVM中不存在该Class的任何实例。
    二、加载该类的ClassLoader已经被GC。
    三、该类对应的java.lang.Class对象没有在任何地方被引用,如不能在任何地方经过反射访问该类的方法。
  • 在大量使用反射、动态代理、CGLib等字节码框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都须要JVM具有类卸载的支持以保证方法区不会溢出。

JVM常见GC算法:

  • 标记-清除算法(Mark-Sweep)
  • 标记-整理算法(Mark-Compact)
  • 复制算法(Copying)
  • 分代算法(Generational)

具体每一个算法下次继续再来学习~~ 

相关文章
相关标签/搜索