zoukankan      html  css  js  c++  java
  • 【JVM】02垃圾回收机制

    垃圾回收

    垃圾回收策略https://blog.csdn.net/u010425776/article/details/51189318

    程序计数器、Java虚拟机栈、本地方法栈都是线程私有的,也就是每条线程都拥有这三块区域,而且会随着线程的创建而创建,线程的结束而销毁。那么,垃圾收集器能够清楚地知道何时清扫这三块区域中的哪些数据。

    然而,堆和方法区中的内存清理工作就没那么容易了。

    1、判断无效对象

    在对堆进行对象回收之前,首先要判断哪些是无效对象。我们知道,一个对象不被任何对象或变量引用,那么就是无效对象,需要被回收。一般有两种判别方式:

    • 引用计数法
      每个对象都有一个计数器,当这个对象被一个变量或另一个对象引用一次,该计数器加一;若该引用失效则计数器减一。当计数器为0时,就认为该对象是无效对象。

    • 可达性分析法
      所有和GC Roots直接或间接关联的对象都是有效对象,和GC Roots没有关联的对象就是无效对象。
      GC Roots是指:

      1. Java虚拟机栈所引用的对象(栈帧中局部变量表中引用类型的变量所引用的对象)
      2. 方法区中静态属性引用的对象
      3. 方法区中常量所引用的对象
      4. 本地方法栈所引用的对象
        PS:注意!GC Roots并不包括堆中对象所引用的对象!这样就不会出现循环引用。

    两者对比:
    引用计数法虽然简单,但存在一个严重的问题,它无法解决循环引用的问题。
    因此,目前主流语言均使用可达性分析方法来判断对象是否有效。

    2、回收算法

    垃圾收集算法

    现在我们知道了判定一个对象是无效对象、判定一个类是废弃类、判定一个常量是废弃常量的方法,也就是知道了垃圾收集器会清除哪些数据,那么接下来介绍如何清除这些数据。

    1. 标记-清除算法

    首先利用刚才介绍的方法判断需要清除哪些数据,并给它们做上标记;然后清除被标记的数据。

    分析:
    这种算法标记和清除过程效率都很低,而且清除完后存在大量碎片空间,导致无法存储大对象,降低了空间利用率。

    2. 复制算法

    将内存分成两份,只将数据存储在其中一块上。当需要回收垃圾时,也是首先标记出废弃的数据,然后将有用的数据复制到另一块内存上,最后将第一块内存全部清除。

    分析:
    这种算法避免了碎片空间,但内存被缩小了一半。
    而且每次都需要将有用的数据全部复制到另一片内存上去,效率不高。

    解决空间利用率问题:
    在新生代中,由于大量的对象都是“朝生夕死”,也就是一次垃圾收集后只有少量对象存活,因此我们可以将内存划分成三块:Eden、Survior1、Survior2,内存大小分别是8:1:1。分配内存时,只使用Eden和一块Survior1。当发现Eden+Survior1的内存即将满时,JVM会发起一次MinorGC,清除掉废弃的对象,并将所有存活下来的对象复制到另一块Survior2中。那么,接下来就使用Survior2+Eden进行内存分配。

    通过这种方式,只需要浪费10%的内存空间即可实现带有压缩功能的垃圾收集方法,避免了内存碎片的问题。

    但是,当一个对象要申请内存空间时,发现Eden+Survior中剩下的空间无法放置该对象,此时需要进行Minor GC,如果MinorGC过后空闲出来的内存空间仍然无法放置该对象,那么此时就需要将对象转移到老年代中,这种方式叫做“分配担保”。


    什么是分配担保?
    当JVM准备为一个对象分配内存空间时,发现此时Eden+Survior中空闲的区域无法装下该对象,那么就会触发MinorGC,对该区域的废弃对象进行回收。但如果MinorGC过后只有少量对象被回收,仍然无法装下新对象,那么此时需要将Eden+Survior中的所有对象都转移到老年代中,然后再将新对象存入Eden区。这个过程就是“分配担保”。

    3. 标记-整理算法

    在回收垃圾前,首先将所有废弃的对象做上标记,然后将所有未被标记的对象移到一边,最后清空另一边区域即可。

    分析:
    它是一种老年代的垃圾收集算法。老年代中的对象一般寿命比较长,因此每次垃圾回收会有大量对象存活,因此如果选用“复制”算法,每次需要复制大量存活的对象,会导致效率很低。而且,在新生代中使用“复制”算法,当Eden+Survior中都装不下某个对象时,可以使用老年代的内存进行“分配担保”,而如果在老年代使用该算法,那么在老年代中如果出现Eden+Survior装不下某个对象时,没有其他区域给他作分配担保。因此,老年代中一般使用“标记-整理”算法。

    4. 分代收集算法

    将内存划分为老年代和新生代。老年代中存放寿命较长的对象,新生代中存放“朝生夕死”的对象。然后在不同的区域使用不同的垃圾收集算法。

     
     
  • 相关阅读:
    SQL Server 2008 R2——VC++ ADO 操作 多线程操作 代码结构
    SQL Server 2008 R2——VC++ ADO 操作 参数化查询
    SQL Server 2008 R2——VC++ ADO 操作 事务
    SQL Server 2008 R2——VC++ ADO 操作 存储过程
    SQL Server 2008 R2——开发资料搜集
    SQL Server 2008 R2——学习/练习/错误/总结/搜集
    CListCtrl中删除多个不连续的行
    C++基础——子类转父类转子类 (派生类转基类转派生类)
    魔改——MDI多视图模板Tab/标签页 初始化/操作控件
    魔改——MFC SDI程序 转换为 MDI程序
  • 原文地址:https://www.cnblogs.com/the-fool/p/11054152.html
Copyright © 2011-2022 走看看