• Java中的垃圾回收机制


    Java中的垃圾回收机制

    来自:Giraffe's Home

    链接:http://yemengying.com/2016/05/13/jvm-GC/(点击尾部阅读原文前往)

    原文:http://javarevisited.blogspot.com/2011/04/garbage-collection-in-java.html

    翻译一篇关于垃圾回收(以下简称GC)机制的博客内容包括:Java中GC是如何工作的,常见的GC算法(比如:标记清除),Java中不同的垃圾收集器(比如:serial)

     

    关键字约定

    • Young generation >新生代

    • Tenured / Old Generation >老年代

    • Perm Area >永久代

     

    重要的东东

    • 在Java中,对象实例都是在堆上创建一些类信息,常量,静态变量等存储在方法区堆和方法区都是线程共享的

    • GC机制是由JVM提供,用来清理需要清除的对象,回收堆内存

    • GC机制将Java程序员从内存管理中解放了出来,可以更关注于业务逻辑

    • 在Java中,GC是由一个被称为垃圾回收器的守护进程执行的

    • 在从内存回收一个对象之前会调用对象的finalize()方法

    • 作为一个Java开发者不能强制JVM执行GC;GC的触发由JVM依据堆内存的大小来决定

    • System.gc()和Runtime.gc()会向JVM发送执行GC的请求,但是JVM不保证一定会执行GC

    • 如果堆没有内存创建新的对象了,会抛出OutOfMemoryError

     

    GC针对什么对象?

    了解GC机制的第一步就是理解什么样的对象会被回收当一个对象通过一系列根对象(比如:静态属性引用的常量)都不可达时就会被回收简而言之,当一个对象的所有引用都为null循环依赖不算做引用,如果对象A有一个指向对象B的引用,对象B也有一个指向对象A的引用,除此之外,它们没有其他引用,那么对象A和对象B都需要被回收(如下图,ObjA和ObjB需要被回收)

     

    堆内存是如何划分的?

    Java中对象都在堆上创建为了GC,堆内存分为三个部分,也可以说三代,分别称为新生代,老年代和永久代其中新生代又进一步分为Eden区,Survivor 1区和Survivor 2区(如下图)新创建的对象会分配在Eden区,在经历一次Minor GC后会被移到Survivor 1区,再经历一次Minor GC后会被移到Survivor 2区,直到升至老年代,需要注意的是,一些大对象(长字符串或数组)可能会直接存放到老年代


    永久代有一些特殊,它用来存储类的元信息对于GC是否发生在永久代有许多不同的看法,在我看来这取决于采用的JVM大家可以通过创建大量的字符串来观察是发生了GC还是抛出了OutOfMemoryError

     

    GC算法

    1标记清除算法


    分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象该算法的缺点是效率不高并且会产生不连续的内存碎片



    2复制算法


    把内存空间划为两个区域,每次只使用其中一个区域垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现碎片问题优点:实现简单,运行高效缺点:会浪费一定的内存一般新生代采用这种算法

    image

    3标记整理算法


    标记阶段与标记清除算法一样但后续并不是直接对可回收的对象进行清理,而是让所有存活对象都想一端移动,然后清理优点是不会造成内存碎片

     

    Java中垃圾回收器的类型

    Java提供多种类型的垃圾回收器JVM中的垃圾收集一般都采用分代收集,不同的堆内存区域采用不同的收集算法,主要目的就是为了增加吞吐量或降低停顿时间

    • Serial收集器:新生代收集器,使用复制算法,使用一个线程进行GC,串行,其它工作线程暂停

    • ParNew收集器:新生代收集器,使用复制算法,Serial收集器的多线程版,用多个线程进行GC,并行,其它工作线程暂停使用-XX:+UseParNewGC开关来控制使用ParNew+Serial Old收集器组合收集内存;使用-XX:ParallelGCThreads来设置执行内存回收的线程数

    • Parallel Scavenge 收集器:吞吐量优先的垃圾回收器,作用在新生代,使用复制算法,关注CPU吞吐量,即运行用户代码的时间/总时间使用-XX:+UseParallelGC开关控制使用Parallel Scavenge+Serial Old收集器组合回收垃圾

    • Serial Old收集器:老年代收集器,单线程收集器,串行,使用标记整理算法,使用单线程进行GC,其它工作线程暂停

    • Parallel Old收集器:吞吐量优先的垃圾回收器,作用在老年代,多线程,并行,多线程机制与Parallel Scavenge差不错,使用标记整理算法,在Parallel Old执行时,仍然需要暂停其它线程

    • CMS(Concurrent Mark Sweep)收集器:老年代收集器,致力于获取最短回收停顿时间(即缩短垃圾回收的时间),使用标记清除算法,多线程,优点是并发收集(用户线程可以和GC线程同时工作),停顿小使用-XX:+UseConcMarkSweepGC进行ParNew+CMS+Serial Old进行内存回收,优先使用ParNew+CMS(原因见Full GC和并发垃圾回收一节),当用户线程内存不足时,采用备用方案Serial Old收集

    可以看Java Performance一书来获取更多关于GC调优的信息

     

    与GC有关的JVM参数

    做GC调优需要大量的实践,耐心和对项目的分析我曾经参与过高容量,低延迟的电商系统,在开发中我们需要通过分析造成Full GC的原因来提高系统性能,在这个过程中我发现做GC的调优很大程度上依赖于对系统的分析,系统拥有怎样的对象以及他们的平均生命周期


    举个例子,如果一个应用大多是短生命周期的对象,那么应该确保Eden区足够大,这样可以减少Minor GC的次数可以通过-XX:NewRatio来控制新生代和老年代的比例,比如-XX:NewRatio=3代表新生代和老年代的比例为1:3需要注意的是,扩大新生代的大小会减少老年代的大小,这会导致Major GC执行的更频繁,而Major GC可能会造成用户线程的停顿从而降低系统吞吐量JVM中可以用NewSize和MaxNewSize参数来指定新生代内存最小和最大值,如果两个参数值一样,那么就相当于固定了新生代的大小


    个人建议,在做GC调优之前最好深入理解Java中GC机制,推荐阅读Sun Microsystems提供的有关GC的文档这个链接可能会对理解GC机制提供一些帮助下面的图列出了各个区可用的一些JVM参数

    jvm参数

     

    Full GC和并发垃圾回收

    并发垃圾回收器的内存回收过程是与用户线程一起并发执行的通常情况下,并发垃圾回收器可以在用户线程运行的情况下完成大部分的回收工作,所以应用停顿时间很短


    但由于并发垃圾回收时用户线程还在运行,所以会有新的垃圾不断产生作为担保,如果在老年代内存都被占用之前,如果并发垃圾回收器还没结束工作,那么应用会暂停,在所有用户线程停止的情况下完成回收这种情况称作Full GC,这意味着需要调整有关并发回收的参数了


    由于Full GC很影响应用的性能,要尽量避免或减少特别是如果对于高容量低延迟的电商系统,要尽量避免在交易时间段发生Full GC

     

    总结

    • 为了分代垃圾回收,Java堆内存分为3代:新生代,老年代和永久代

    • 新的对象实例会优先分配在新生代,在经历几次Minor GC后(默认15次),还存活的会被移至老年代(某些大对象会直接在老年代分配)

    • 永久代是否执行GC,取决于采用的JVM

    • Minor GC发生在新生代,当Eden区没有足够空间时,会发起一次Minor GC,将Eden区中的存活对象移至Survivor区Major GC发生在老年代,当升到老年代的对象大于老年代剩余空间时会发生Major GC

    • 发生Major GC时用户线程会暂停,会降低系统性能和吞吐量

    • JVM的参数-Xmx和-Xms用来设置Java堆内存的初始大小和最大值依据个人经验这个值的比例最好是1:1或者1:1.5比如,你可以将-Xmx和-Xms都设为1GB,或者-Xmx和-Xms设为1.2GB和1.8GB

    • Java中不能手动触发GC,但可以用不同的引用类来辅助垃圾回收器工作(比如:弱引用或软引用)

    以上就是关于Java中GC的一些内容通过这篇博客,我们可以知道堆内存是如何划分的;一个对象在没有任何强引用指向他或该对象通过根节点不可达时需要被垃圾回收器回收;当垃圾收集器意识到需要进行GC时会触发Minor GC或Major GC,是自动的,无法强制执行

    参考文档(康桑阿米达~)

    • http://icyfenix.iteye.com/blog/715****01

    • http://www.cnblogs.com/zhguang/p/325****67.html

    • 深入理解java虚拟机

    Java内存模型与垃圾回收

    Java垃圾回收机制

    Java垃圾收集学**笔记

    Java中的垃圾回收

    在Java中谈尾递归--尾递归和垃圾回收的比较

  • 相关阅读:
    Qt之模型/视图(自己定义button)
    Spring入门--控制反转(IOC)与依赖注入(DI)
    iOS关闭键盘简单实现(objc/swift)
    事件传递机制总结
    Skill of vim
    Java中ArrayList和LinkedList区别
    java截取url中的值
    MySQL主从复制与主主复制
    MySQL读写分离
    Mysql-Proxy代理配置
  • 原文地址:https://www.cnblogs.com/timssd/p/11167964.html
Copyright © 2020-2023  润新知