java垃圾回收机制

概述

  1. Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存,而且这两个问题针对的内存区域就是Java内存模型中的堆区
  2. JVM内存模型一共包括了三个部分:堆(Java代码可及的Java堆和JVM自身使用的方法区)、栈(服务Java方法的虚拟机栈和服务Native方法的本地方法栈)和保证程序在多线程环境下能够连续执行的程序计数器,而堆就是垃圾回收的主要对象。
  3. 垃圾回收机制主要涉及的三个问题
    1. 哪些内存需要回收?
    2. 什么时候回收?
    3. 如何回收?

哪些内存需要回收?

  1. 引用计数算法:判断对象的引用数量
    1. 引用计数算法是垃圾收集器中的最早策略,在这种方法中,堆中的每个对象实例都有一个引用计数器,当一个对象被创建时,且被分配给一个引用变量,那么该对象实例的引用计数器设置为1,当任何其他变量被赋值为这个对象的引用时,对象实例的引用计数加1。当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数减1,特别地,当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1
    2. 引用计数器无法解决对象之间互相引用的问题,如果对象a和对象b互相引用,那么它们身上的引用计数器永远不可能为0,那么这两个对象就永远不可能被回收
  2. 可达性分析算法:判断对象的引用链是否可达
    1. 从一系列名为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连时,证明此对象是不可用的
    2. GC Root的对象包括以下
      1. 虚拟机栈(栈帧中的局部变量表)中引用的对象
      2. 方法区中类静态属性引用的对象
      3. 方法区中常量引用的对象
      4. 本地方法栈中Native方法引用的对象

如何回收?

  1. 标记清除算法
    1. 标记-清除算法分为标记和清除两个阶段,该算法首先从根集合进行扫描,对存活的对象标记,标记完毕后,再扫描整个空间中未被标记的对象进行回收
    2. 缺点
      1. 效率问题:标记和清除两个过程的效率都不高
      2. 空间问题:标记-清除算法不需要进行对象移动,仅对不存活的对象进行处理,因此标记清除之后会产生大不连续的内存碎片,空间碎片太多可能会导致程序运行过程中如果需要分配较大的对象时,不能找到足够的连续内存而不得不提前触发一次垃圾收集动作
  2. 复制算法
    1. 复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次性清理掉
    2. 适用于对象存活率低的场景,比如新生代,这样使得每次都是对整个半区进行内存回收,不用考虑内存碎片的情况
    3. 事实上,现在商用的虚拟机都采用这种算法来回收新生代。因为研究发现,新生代中的对象每次回收都基本上只有10%左右的对象存活,所以需要复制的对象很少,实践中会将新生代内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是 8:1,也就是每次新生代中可用内存空间为整个新生代容量的90% ( 80%+10% ),只有10% 的内存会被“浪费”
  3. 标记整理算法
    1. 复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。标记整理算法的标记过程类似标记清除算法,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,类似于磁盘整理的过程,该垃圾回收算法适用于对象存活率高的场景(老年代)
    2. 标记整理算法与标记清除算法最显著的区别是:标记清除算法不进行对象的移动,并且仅对不存活的对象进行处理;而标记整理算法会将所有的存活对象移动到一端,并对不存活对象进行处理,因此其不会产生内存碎片。

为什么要优化GC?

  1. 在JVM执行GC的时候,会停止所有用户线程,直到垃圾收集完成,这种暂停是为了保证垃圾收集的正确性和效率
  2. Stop-the-World的过程会影响到Java应用的性能和吞吐量,因此,Java虚拟机在设计和实现垃圾收集器时,会尽量地减少Stop-the-World的时间和频率,以提高应用的吞吐量,所以我们优化垃圾收集的目的,也可以说为了降低Stop-the-World的发生

内存泄露和内存溢出

  1. 内存泄漏:一个内存对象的生命周期结束之后未被正确回收,也可以说一个内存对象的声明周期超过了程序需要它的时间长度
  2. 内存溢出:
    1. 程序运行过程中,无法分配更多的内存空间,导致程序异常终止或者运行异常缓慢,通常是由于内存分配或回收机制出现问题,或者程序本身存在内存泄漏的问题
    2. java内存溢出可以分为两种
      1. 堆内存溢出:当Java堆内存中的空间不足以满足程序需求时,Java虚拟机就会抛出Heap Speace错误的异常,这种错误通常发生在程序运行过程中,由于创建的对象过多或者内存回收不及时,导致堆内存占满
      2. 栈内存溢出:当Java栈中的线程数量过多或者每个线程使用的栈空间超过了Java虚拟机所允许的最大值时,就会发生Stack Speace错误的异常,这种错误通常发生在递归调用过多的函数,或者线程使用的局部变量过多等情况下
  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值