Java GC机制及相关

    记录学习路上的所见,如有纰漏还望多多包涵。                                                                                       Alice


垃圾(Garbage)就是程序需要回收的对象,如果一个对象不在被直接或间接地引用,那么这个对象就成为了「垃圾」,它占用的内存需要及时地释放,否则就会引起「内存泄露」。有些语言需要程序员来手动释放内存(回收垃圾),有些语言有垃圾回收机制(GC),例如我正在学习的Java语言,存在垃圾回收机制。

在学习Java GC 之前,我们需要记住一个单词:stop-the-world 。它会在任何一种GC算法中发生。stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行。当stop-the-world 发生时,除GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成。GC优化很多时候就是减少stop-the-world 的发生,缩短因GC线程中断时间,保证程序运行的效率。

第一种   标记清除

标记清除(Mark and Sweep)是最早开发的GC算法。

原理:两次扫描,第一次从根开始将可能被引用的对象进行以递归的方式进行标记标记,第二次将未标记的对象进行回收,回收成功后将标记的引用对象清除标记为下次的GC回收做准备。

GC时间:与存活对象数和程序对象总数有关,因为要对所有对象进行递归查看是否可以进行回收。

思考:如果存活对象的数量少,那么标记阶段的实际标记很少,剩下的全为不在引用的对象,那么标记阶段的效率便会降低,从而整个GC过程的效率也会降低。

标记清除的方式节省内存但是两次扫描需要更多的时间,对于垃圾比例较小的情况占优势。

第二种  复制收集

复制收集(Copy and Collection)会将从根开始被引用的对象复制到另外的空间中去,然后再舍弃掉旧的空间,利用新的空间。

原理:复制收集的方式只需要对对象进行一次扫描。准备一个「新的空间」,从根开始,对对象进行扫,如果存在对这个对象的引用,就把它复制到「新空间中」。一次扫描结束之后,所有存在于「新空间」的对象就是所有的非垃圾对象。

思考:复制收集具有局部性,在复制收集的过程中,会按照对象被引用的顺序将对象复制到新空间中。于是,关系较近的对象被放在距离较近的内存空间的可能性会提高,这叫做局部性。局部性高的情况   下,内存缓存会更有效地运作,程序的性能会提高。

复制收集更快速但是需要额外开辟一块用来复制的内存,对垃圾比例较大的情况占优势。

第三种     引用计数

引用计数是指,针对每一个对象,保存一个对该对象的引用计数,该对象的引用增加,则相应的引用计数增加。如果对象的引用计数为零,则回收该对象。

原理:每个对象中保存该对象的引用计数,当引用发生增减时对计数进行更新,发生时间点:变量赋值、对象内容更新、函数结束(局部变量不再被引用)。当一个对象的引用计数为零时则说明它不再被引用,因此释放相应的内存空间。

  

思考:引用计数最大的优点就是容易实现。成本小,基本上引用计数为0的时候垃圾会被立即回收,而其他方法难以预测对象的生命周期,垃圾存在的时间都会比这个方法长。这种垃圾回收方式产生的中断时间最短。

但如果对象中存在循环引用,就无法被回收。引用计数不适合在并行中使用,多个线程同时操作引用计数,会引起数值不一样的问题从而导致内存错误。所以引用计数必须采用独占方式,如果引用操作频繁,那么加锁等并发控制机制的开销是相当大的。


Java GC(垃圾回收机制)

JVM GC只回收堆区和方法区内的对象。而栈区的数据,在超出作用域后会被JVM自动释放掉,所以其不在JVM GC的管理范围内。

  判定对象是否可回收的标准:

  1. 对象没有引用
  2. 作用域发生未捕获异常
  3. 程序在作用域正常执行完毕
  4. 程序执行了System.exit()
  5. 程序发生意外终止(被杀线程等)

  在Java程序中不能显式的分配和注销缓存,因为这些事情JVM都帮我们做了,那就是GC。

  有些时候我们可以将相关的对象设置成null 来试图显示的清除缓存,但是并不是设置为null 就会一定被标记为可回收,有可能会发生逃逸。

  将对象设置成null 至少没有什么坏处,但是使用System.gc() 便不可取了,使用System.gc() 时候并不是马上执行GC操作,而是会等待一段时间,甚至不执行,而且System.gc() 如果被执行,会触发Full GC ,这非常影响性能。



Java的垃圾回收是分代回收的,分为三代:新生代,老年代,持久代。

  新生代(Young generation):绝大多数最新被创建的对象都会被分配到这里,由于大部分在创建后很快变得不可达,很多对象被创建在新生代,然后“消失”。对象从这个区域“消失”的过程我们称之为:Minor GC 。

老年代(Old generation):对象没有变得不可达,并且从新生代周期中存活了下来,在新生代的战场上奋勇存活下来的对象,是不会轻易狗带的,嘻嘻。会被拷贝到这里。其区域分配的空间要比新生代多。也正由于其相对大的空间,发生在老年代的GC次数要比新生代少得多。对象从老年代中消失的过程,称之为:Major GC 或者 Full GC。

持久代(Permanent generation)也称之为 方法区(Method area):用于保存类常量以及字符串常量。注意,这个区域不是用于存储那些从老年代存活下来的对象,这个区域也可能发生GC。发生在这个区域的GC事件也被算为 Major GC 。只不过在这个区域发生GC的条件非常严苛,必须符合以下三种条件才会被回收:

1、所有实例被回收

2、加载该类的ClassLoader 被回收

3、Class 对象无法通过任何途径访问(包括反射)

新生代和老年代的结构:

  新生代空间的构成与逻辑

为了更好的理解GC,我们来学习新生代的构成,它用来保存那些第一次被创建的对象,它被分成三个空间:

        · 一个伊甸园空间(Eden)

       · 两个幸存者空间(Fron Survivor、To Survivor)

       默认新生代空间的分配:Eden : Fron : To = 8 : 1 : 1

每个空间的执行顺序如下:

1、绝大多数刚刚被创建的对象会存放在伊甸园空间(Eden)。

2、在伊甸园空间执行第一次GC(Minor GC)之后,存活的对象被移动到其中一个幸存者空间(Survivor)。

3、此后,每次伊甸园空间执行GC后,存活的对象会被堆积在同一个幸存者空间

4、当一个幸存者空间饱和,还在存活的对象会被移动到另一个幸存者空间。然后会清空已经饱和的哪个幸存者空间。

5、在以上步骤中重复N次(N = MaxTenuringThreshold(年龄阀值设定,默认15))依然存活的对象,就会被移动到老年代。

从上面的步骤可以发现,两个幸存者空间,必须有一个是保持空的。如果两个两个幸存者空间都有数据,或两个空间都是空的,那一定是你的系统出现了某种错误。

我们需要重点记住的是,对象在刚刚被创建之后,是保存在伊甸园空间的(Eden)。那些长期存活的对象会经由幸存者空间(Survivor)转存到老年代空间(Old generation)。

      也有例外出现,对于一些比较大的对象(需要分配一块比较大的连续内存空间)则直接进入到老年代。一般在Survivor 空间不足的情况下发生。

   老年代空间的构成与逻辑

老年代空间的构成其实很简单,它不像新生代空间那样划分为几个区域,它只有一个区域,里面存储的对象并不像新生代空间绝大部分都是朝闻道,夕死矣。这里的对象几乎都是从Survivor 空间中熬过来的,它们绝不会轻易的狗带。因此,Full GC(Major GC)发生的次数不会有Minor GC 那么频繁,并且做一次Major GC 的时间比Minor GC 要更长(约10倍)。

                                                                                    2018.01.30   记


原文地址:https://www.cnblogs.com/Alice-Thinker/p/8384507.html

时间: 2024-10-18 18:44:07

Java GC机制及相关的相关文章

Java GC机制和对象Finalize方法的一点总结

GC是什么? 为什么要有GC? GC是垃圾收集的意思(Garbage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的. 有向图垃圾回收机制 .NET的垃圾回收采用引用计数,java的垃圾回收机制采取的是有向图的方式来实现,具体的说,java程序中的每个线程对象就可以看作是一个有向图的起点,有向边从栈中的引用者指向堆中的引用对象.在这个有向图中,如果

Java GC机制

Java中GC机制(garbage collection)是垃圾回收机制,更确切的说是内存回收机制 在一个对象不再被程序引用时,它所占用的堆空间就可以回收,以便分配给新对象使用.而且除了释放不再被引用的对象外,垃 圾收集器还要处理堆碎块(堆碎块是在正常的程序运行时产生的),因为新的对象分配了空间,不再被引用的对象被释放,所以堆内存的空闲位置介于活对的对象之间,而请求分配新对象时可能不得不增大堆空间,因为虽然总的空闲空间是够的,但堆中没有连续的空闲空间放得下新对象. 上面是垃圾收集的作用,其好处在

Java GC 机制与内存分配策略

收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现 自动内存管理解决的是:给对象分配内存 以及 回收分配给对象的内存 为什么我们要了解学习 GC 与内存分配呢? 在 JVM 自动内存管理机制的帮助下,不再需要为每一个new操作写配对的delete/free代码.但出现内存泄漏和溢出的问题时,如果不了解虚拟机是怎样使用内存的,那么排查错误将是一项非常艰难的工作. GC(垃圾收集器)在对堆进行回收前,会先确定哪些对象"存活",哪些已经"死去".那么就有了对象存活

Java 内存区域和GC机制

目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对 JVM(Java Virtual M

Java内存区域划分和GC机制

Java 内存区域和GC机制 目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对 JVM

Java 内存区域和GC机制(转)

Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对 JVM(Java Virtual Machine)中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息(Nerver Stop

Java GC回收机制

优秀Java程序员必须了解的GC工作原理 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能. 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.本篇文章首先简单介绍GC的工

Java内存区域和GC机制

Java内存区域 了解Java GC机制,必须先清楚在JVM中内存区域的划分.在Java运行时的数据区里,由JVM管理的内存区域分为下图几个模块: 其中: 1,程序计数器(Program Counter Register):程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器.字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令. 每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的. 如

Java笔记-Java 内存区域和GC机制

此次主要做一下java内存区域和GC机制的知识小结,下面的段落部分摘自网络,部分摘自书籍,具体的链接忘记了,因为这是之前学习的时候做下的笔记.还望原作者多多包涵! 1 Java垃圾回收 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机(JVM)中,存在自动内存管理和垃圾清扫机制.概括地说