JVM3

时间: 2023-07-18 admin 互联网

JVM3

JVM3

一、垃圾回收概述 

1、垃圾对象:运行程序中没有任何指针指向的对象

2、垃圾回收区域: 方法区+堆空间

       频繁回收Young区

       较少回收old区

       基本不动Perm区(或元空间)

3、垃圾回收的两个阶段

  (1)标记阶段

        引用计数算法

        可达性分析算法

 (2)清除阶段

       标记-清除算法

       标记-复制算法

       标记-压缩算法

4、运行时数据区垃圾回收情况

   (1)方法区------------有GC+OOM(outofmemory内存溢出,堆溢出)

    (2)----------------有GC+OOM

    (3)程序计数器-------都没有

    (4)本地方法栈-------没有GC,有SOF(stack over flow栈溢出)

    (5)虚拟机栈------------没有GC,有SOF

5、内存溢出(OOM)

  (1)定义

         没有空闲的内存,且垃圾回收器也无法提供更多的内存

  (2)产生原因

  •    JVM的堆内存设置不够

          内存溢出一般指堆内存,一般也会由内存泄露导致内存不足

  •    创建了大量大对象,且存在被引用,长时间不能被GC收集

   (3)内存溢出是引发程序崩溃的原因之一

6、内存泄露(Memory Leak)

 (1)定义

    该对象不会再被程序用到了,但GC又不能回收他们

 (2)产生原因

    由于编码的习惯或疏忽,导致对象的声明周期很长,导致OOM---宽泛意义上内存泄露,但是存成了

   如:局部变量可以解决的,结果声明成全局变量或静态变量;有些对象没必要存到session区域,成会话级别的对象

  (3)内存泄露可能导致OOM,在超出内存空间时,才会导致

7、STM--stop the world

(1)定义

        只GC时间发生过程中,会产生应用程序的停顿

        停顿产生时,整个应用程序线程都会被暂停,没有任何响应。

(2)发生STW的场景

        可达性分析算法中,枚举根节点会导致所有java执行线程停顿

(3)STW一般是由JVM在后台自动发起或自动完成的

        手动调用System.gc()也会导致SWT的发生

8、Systen.gc() 

(1)触发Full GC

  •    通过调用System.gc()或Runtime.getRuntime.gc()显示触发Full GC
  •    Full GC:整个堆,对老年代和新生代同时进行回收,尝试释放被丢弃的对象所占用的内存

(2)Systen.gc()可以触发Full GC进行垃圾回收,但是不确定是否马上执行gc

(3)一般是自动垃圾回收,很少手动调用Systen.gc()

9、引用--强软弱虚   java.lang.ref

  • 强引用(Strong Reference)---不回收

  (1)特点:

       无论任何情况下,只要强引用关系还存在,垃圾回收器就永远不会回收被引用的对象

         如new的对象

 (2)强引用类型对象回收条件:

         没有引用关系或显示将引用复制为null

  (3)强引用是造成java内存泄露的主要原因之一

  • 软引用(Soft Reference)---内存不足即回收

(1)特点:

      在系统将要发生内存溢出时,将存在此类引用的对象二次回收掉

     (一次回收将不可达对象的回收)

(2)软引用对象是非必需的对象

  • 弱引用(Weak Reference)---发现即回收

(1)特点:

      只被该引用关联的对象只能存活到下一次垃圾收集之前,无论引用是否存在

(2)软引用对象是非必需的对象

(3)可以用来保存可有可无的缓存数据

         内存缓存(一级)----本地缓存(二级)---网络缓存(三级)

(4)WeakHashMap---利用的弱引用

                                     可以在内存不足时,及时回收数据,避免用强引用导致OOM发生

          HashMap---利用强引用

  • 虚引用(Phantom Reference)--对象回收跟踪

(1)特点:

     一个对象是否有虚引用,完全不会影响一个对象的生存时间

(2)唯一目的

     在这个对象被收集器回收时接收到一个系统通知,相当于没有引用,设置个回收提醒而已

   [注]引用关系还在情况下,GC机制对带有以上不同引用的对象处理

二、垃圾标记阶段

 目的:区分出内存中哪些是存活对象,哪些是死亡对象

           当一个对象已经不在被任何的存活对象继续引用时,就是垃圾对象

(一)引用计数法(java中不用)

1、每个对象保存一个整型的引用计数器属性,用于记录对象被引用情况

2、当一个对象引用该对象,引用计数器就+1,当引用失效时,引用计数器就-1

    只要对象的引用计数器的值为0,则表示对象可进行回收

3、缺点

   (1)需要单独的字段存储计数器,增加空间开销 

   (2)经常更新计数器,增加增加开销

   (3)无法处理循环引用情况

(二)可达性分析(根搜索算法,追踪性垃圾收集)

1、可以解决循环引用问题

2、使用条件

    保证数据一致性

2、原理:

   以跟对象集合为起始点(GC Roots),

   按从上至下的方式搜索被根对象集合所连接的对象是否可达

   可达---非垃圾;不可达---垃圾

3、根对象集合(GC Roots )

   一组必须活跃的引用,主要有以下几类元素:

 (1)Java虚拟机栈中引用的对象 :局部变量

 (2)本地方法栈中引用的对象:引用类型静态变量,类变量

   (3)方法区中引用的常量对象:字符串常量池里的引用

 (4)被同步锁synchronized持有的对象

   (5)分代收集和局部回收

4、MAT---可以查询GC Root  

     一款强大的Java堆内存分析器

 5、对象的状态

       可触及----不是垃圾

       可复活---是垃圾,但是可以通过调用重写的finalize()复活

       不可触及--是垃圾,且已经调用了finalize(),但这个方法只能被调用一次,因此不可再复活

三、垃圾回收阶段

(一)标记-清除算法(Mark-Sweep)

   1、原理

      (1)当堆内存中的有效内存空间被耗尽时,就会停止整个程序(stop the world)

      (2)标记非垃圾对象:Cllector从引用根节点(GC Root)开始遍历,标记所有被引用的对象,在对象header中记录为可达对象

      (3)清除垃圾对象:Cllector遍历堆内存中所有对象,header中没有被标记为可达对象的,则被回收清除

   2、缺点:

    (1)需要停止整个应用程序,用户体验差

    (2)清理出的空闲内存不连续,产生内存碎片----需要维护空闲列表

    (3)遍历效率不算高,需要遍历两次

  3、清除

      清除不是真的置空,而是将需要清除的对象地址保存在空闲的地址列表中 

      如果有新对象需要加载时,先判断垃圾空闲位置空间是否够,够就用新对象覆盖原来数据

(二)复制算法

     1、原理:

        (1)将内存分为两块,每次只使用其中一块

        (2)垃圾回收时,将正则使用的内存中存活对象复制到未被使用的内存块中,之后清除当前正在使用的内存块中所有对象,

               循环交换,完成垃圾回收

  2、优缺点:

   优:没有标记和清除过程,简单高效

        空间连续,不会出现碎片问题,可采用指针碰撞方式分配对象

  缺:需要两倍的内存空间

       只适合垃圾对象比较多的场景,这样复制算法复制的对象数量不会太大---适合在新生代中使用,不适合在老年代中使用

(三)标记-压缩算法(整理,mark-compact)

   1、原理

   (1)从根节点标记所有被引用的对象

   (2)将所有存活的对象整理到内存的一端,按顺序排放

   (3)清除所有未标记的垃圾对象

  2、优缺点

   优:解决了上两个算法的问题:对碎片化空间进行了整理,消除了复制算法的内存减半的代价

   缺点:效率偏低,移动对象的同时,引用该对象的其他对象要调整引用的地址

           移动过程中需要全程暂停用户程序 STW

四、回收算法的应用

(一)分代收集算法

   1、新生代---回收频繁---复制算法

   2、老年代---回收不频繁---标记-清除或标记-整理混合实现

(二)增量收集算法---降低延迟(GC产生的停顿时间)

  1、作用

       因为一次性处理所有垃圾时,会停掉所有程序,这造成系统长时间的停顿

       解决STM(stop the world)状态下的时间过长问题,即延迟时间较长      

 2、基本思想:

    每次垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程

    让垃圾收集线程和应用程序线程交替执行,直至垃圾收集完成

3、使用的GC算法

    标记-清除和复制算法

    通过对线程间冲突的妥善处理,允许垃圾收集线程以分阶段的方式完成标记、清理或复制工作

 4、缺点:

     线程切换和上下文切换的消耗,使垃圾回收的总体成本上升,造成系统吞吐量下降

       

(三)分区算法-----降低延迟(GC产生的停顿时间)

1、作用

   主要解决GC所需要的停顿时间问题(STW)---降低延迟

2、基本思想

  (1)将一块大的内存区域分割成多个小块

  (2)设置目标停顿时间,根据这个时间每次合理地回收若干个小区间(region)

3、分区

    将一整块堆空间分成若干个region,不同的region盛放不同对象,如某几个region放同一类对象

    Eden---边缘区数据

    Survivor---新生代数据

    Old------老年代数据

    Humongous----大对象

   4、回收过程:

  (1)每次new对象时,会先放在eden区

     (2)当年轻代的Eden区快满时,出发Yong GC,发生STW标记可达对象,是垃圾---清除

     (3)将其中存活的对象复制移动到s0或s1区,谁空放在谁那里(to区),

         然后遍历标记另一survivor区,垃圾清除,存活则移动到to区(复制算法 )

   (4)遍历时,form区的对象被回收超过15次依旧存活时(年龄计数器值=15),就转移到老年区

[注]

  • s0和s1可分为from区和to区

         谁空谁是to区,循环调换

  •   Young GC只有当eden区满的时候才会触发,survivor区满不会触发
  •   老年代:年龄值15

       老年代空间不足时先触发Major GC----还不足OOM

5、不同GC触发条件

      <1>Young GC (Minor GC)---年轻代中eden区满【年轻代回收】

      <2>Old GC (Major GC)---老年代区满【老年代回收】

       <3>Full GC ---年轻代中eden区满【整个堆回收,包括年轻代和老年代】

6、触发Full GC条件

  •  调用System.gc( )
  • 老年代空间不足
  • 方法区空间不足
  • Minor GC后,进入老年代的平均大小>老年代可用内存