精华内容
下载资源
问答
  • 年轻代厨房小家电洞察报告.pdf
  • 年轻代厨房小家电洞察报告-阿里研究院.pdf
  • 在此我们将对象分为三种状态:年轻代、年老代、永久代。同时将处于不同状态的对象放到堆中的不同区域。JVM将堆内存分为Eden、Survivor和Tenured/Old空间,其中Eden和Survivor属于年轻代,Tenured/Old属于年老代。

    今天在学习垃圾回收机制的时候老师引用了一个很形象的例子来理解分代机制,所以我想跟大家分享一下,让我们来一起看看吧。

    首先我们需要明白分代垃圾回收机制是基于这样一个事实:不同的对象的生命周期是不一样的。因此不同生命周期的对象可以采用不同的回收算法,以便于提高回收效率。在此我们将对象分为三种状态:年轻代、年老代、永久代。同时将处于不同状态的对象放到堆中的不同区域。JVM将堆内存分为Eden、Survivor和Tenured/Old空间,其中Eden和Survivor属于年轻代,Tenured/Old属于年老代。有朋友可能会有疑问,永久代哪去了呢,别着急,我们最后在解释永久代的问题.

    接下来就让我们先来看看这个例子吧。我们可以把整个堆内存当成一个饭店,里面有包间、大厅(Tenured)还有盒饭区域(Eden和Survivor)。盒饭区域就是来了赶紧吃,吃完赶紧走人,人员流动性比较大;大厅的话相对来说流动性没有那么大,你可以喝点小酒慢慢吃;而包间时间就更长了,你可以和几个朋友吃饭喝酒聊天,直到饭店打烊再走也没关系。

    所有新来的顾客(所有新生成的对象)都会先被安排在盒饭区的Eden区,直到Eden区满了以后就需要我们的小服务员(Minor GC)上场了,我们的小服务员不需要太多资源,随叫随到,她会把Eden区全部的对象都回收,如果是吃完了的顾客(无用的对象)会被清除,那些没有吃完的顾客(还在使用的对象)会被放到Survivor1区,然后Eden区会存入新的对象,如此往复,无用的清除,有用的放入S1区。直到S1区满了以后,小服务员会再看一看S1区谁吃完了,谁没吃完,吃完了的会被清除,没有吃完的会被请到S2区接着吃,把S1区清空。
    在这里插入图片描述
    但是,S2区也会有满的时候。当S2区满了以后,小服务员会把那些还没有吃完的顾客再次放入S1区。如此往复,当S1区与S2区交替15次后,如果还存在没有吃完的顾客(有用的对象)的话,此时小服务员就会把他们放入大厅区域(此时对象就进入了年老代)。
    在这里插入图片描述
    当对象进入年老代之后,他的回收机制就不归小服务员管了,此时就该大服务员Major GC和Full GC登场了,而大服务员的启动相对小服务员来说就没有那么快了,耗费的资源也多,所以是直到大厅都满了以后才会启动大服务员。可能会有朋友疑问,Major GC和Full GC有什么区别呢?Major GC和Full GC的区别就在于Major GC只清理大厅的顾客,而Full GC则会来一次大扫除,她会把大厅和盒饭区域全部都给清理了。

    当然还有一些顾客,不管什么时候来,直到晚上饭店打烊才走,这样的顾客就会把他放入包间,也就是说JVM一开就存在的对象,直到JVM关掉才释放,就会被存入永久代。永久代里存放的一般是静态文件,比如Java类、方法、常量等,他对垃圾回收没有显著影响。JDK7以前永久代就是方法区的一种实现,而在JDK8及以后就把永久代给舍弃了,转而用metaspace元数据空间和堆替代。
    在这里插入图片描述

    总结
    Minor GC:用于清理年轻代区域,当Eden区满了之后就会触发一次Minor GC,清理无用的对象,将有用的对象复制到S1和S2区中。
    Major GC:用于清理年老代区域。
    Full GC:用于清理年轻代和年老代区域,成本较高,会对系统性能产生影响。

    如果有不对的地方望各位大佬指正,如果觉得有用的话,希望各位大佬点点关注,感谢

    展开全文
  • 如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。 回收器选择 JVM给了三种选择:串行收集器、并行收集器、并发...

    从网上找到这篇文章,希望自己对Java的内存分配有重新的认识

    /usr/local/jdk/bin/java -Dresin.home=/usr/local/resin -server 

    -Xms1800M 

    -Xmx1800M 

    -Xmn300M 

    -Xss512K 

    -XX:PermSize=300M 

    -XX:MaxPermSize=300M 

    -XX:SurvivorRatio=8 

    -XX:MaxTenuringThreshold=5 

    -XX:GCTimeRatio=19 

    -Xnoclassgc 

    -XX:+DisableExplicitGC 

    -XX:+UseParNewGC 

    -XX:+UseConcMarkSweepGC 

    -XX:+UseCMSCompactAtFullCollection 

    -XX:CMSFullGCsBeforeCompaction=0 

    -XX:-CMSParallelRemarkEnabled 

    -XX:CMSInitiatingOccupancyFraction=70 

    -XX:SoftRefLRUPolicyMSPerMB=0 

    -XX:+PrintClassHistogram 

    -XX:+PrintGCDetails 

    -XX:+PrintGCTimeStamps 

    -XX:+PrintHeapAtGC 

    -Xloggc:log/gc.log

     

     

    1. 堆大小设置
      JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。
      典型设置:
      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
        -Xmx3550m:设置JVM最大可用内存为3550M。
        -Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
        -Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
        -Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
      • java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
        -XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
        -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
        -XX:MaxPermSize=16m:设置持久代大小为16m。
        -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
    2. 回收器选择
      JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。
      1. 吞吐量优先的并行收集器
        如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。
        典型配置:
        • java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
          -XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
          -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
        • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
          -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。
        • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100
          -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
        • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100-XX:+UseAdaptiveSizePolicy
          -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。
    3. 响应时间优先的并发收集器
      如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
      典型配置:
      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
        -XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
        -XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。
      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
        -XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
        -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

    辅助信息
    JVM提供了大量命令行参数,打印信息,供调试使用。主要有以下一些:

    • -XX:+PrintGC
      输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]

                      [Full GC 121376K->10414K(130112K), 0.0650971 secs]

    • -XX:+PrintGCDetails
      输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]

                      [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

    • -XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用
      输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
    • -XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
      输出形式:Application time: 0.5291524 seconds
    • -XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用
      输出形式:Total time for which application threads were stopped: 0.0468229 seconds
    • -XX:PrintHeapAtGC:打印GC前后的详细堆栈信息
      输出形式:
      34.702: [GC {Heap before gc invocations=7:
      def new generation   total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K, 99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)
      from space 6144K, 55% used [0x221d0000, 0x22527e10, 0x227d0000)
      to   space 6144K,   0% used [0x21bd0000, 0x21bd0000, 0x221d0000)
      tenured generation   total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)
      compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:
      def new generation   total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,   0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)
      from space 6144K, 55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)
      to   space 6144K,   0% used [0x221d0000, 0x221d0000, 0x227d0000)
      tenured generation   total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)
      compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      }
      , 0.0757599 secs]
    • -Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。

    常见配置汇总

    1. 堆设置
      • -Xms:初始堆大小
      • -Xmx:最大堆大小
      • -XX:NewSize=n:设置年轻代大小
      • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
      • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
      • -XX:MaxPermSize=n:设置持久代大小

    收集器设置

    • -XX:+UseSerialGC:设置串行收集器
    • -XX:+UseParallelGC:设置并行收集器
    • -XX:+UseParalledlOldGC:设置并行年老代收集器
    • -XX:+UseConcMarkSweepGC:设置并发收集器

    垃圾回收统计信息

    • -XX:+PrintGC
    • -XX:+PrintGCDetails
    • -XX:+PrintGCTimeStamps
    • -Xloggc:filename

    并行收集器设置

    • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
    • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
    • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

    并发收集器设置

    • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
    • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

    四、调优总结

    1. 年轻代大小选择
      • 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
      • 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
    2. 年老代大小选择
      • 响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
        • 并发垃圾收集信息
        • 持久代并发收集次数
        • 传统GC信息
        • 花在年轻代和年老代回收上的时间比例
      减少年轻代和年老代花费的时间,一般会提高应用的效率
    3. 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
    4. 较小堆引起的碎片问题
      因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
      • -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
      • -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
    5. jvm的内存限制

            windows2003是1612M

     

    PS:年轻代,老年带,持久代在这里在解释一下

     

    虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。

    年轻代:

    所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

    年老代:

    在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

    持久代:

    用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。

     

    Scavenge GC

    一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

    Full GC

    对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:

    · 年老代(Tenured)被写满

    · 持久代(Perm)被写满

    · System.gc()被显示调用

    ·上一次GC之后Heap的各域分配策略动态变化

     

    添加一些信息:

     

    -XX:+CMSClassUnloadingEnabled

     

           这个参数表示在使用CMS垃圾回收机制的时候是否启用类卸载功能。默认这个是设置为不启用的,所以你想启用这个功能你需要在Java参数中明确的设置下面的参数:

    -XX:+CMSClassUnloadingEnabled

    如果你启用了CMSClassUnloadingEnabled ,垃圾回收会清理持久代,移除不再使用的classes。这个参数只有在 UseConcMarkSweepGC  也启用的情况下才有用。参数如下:

    -XX:+UseConcMarkSweepGC

    -XX:+CMSPermGenSweepingEnabled

           这个参数表示是否会清理持久代。默认是不清理的,因此我们需要明确设置这个参数来调试持久代内存溢出问题。这个参数在Java6中被移除了,因此你需要使用 -XX:+CMSClassUnloadingEnabled 如果你是使用Java6或者后面更高的版本。那么解决持久代内存大小问题的参数看起来会是下面这样子:

    -XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC XX:+CMSClassUnloadingEnabled

     

    -XX:+CMSParallelRemarkEnabled 降低标记停顿

    展开全文
  • 关键字约定 Young generation –>...年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的...

    关键字约定

    Young generation –>新生代
    Tenured / Old Generation –>老年代
    Perm Area –>永久代

    年轻代:

    所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

    年老代:

    在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

    持久代:

    用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。

    重要的东东
    在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需要被回收)。
    GC回收对象GC回收对象

    堆内存是如何划分的?
    Java中对象都在堆上创建。为了GC,堆内存分为三个部分,也可以说三代,分别称为新生代,老年代和永久代。其中新生代又进一步分为Eden区,Survivor 1区和Survivor 2区(如下图)。新创建的对象会分配在Eden区,在经历一次Minor GC后会被移到Survivor 1区,再经历一次Minor GC后会被移到Survivor 2区,直到升至老年代,需要注意的是,一些大对象(长字符串或数组)可能会直接存放到老年代。
    堆内存堆内存
    永久代有一些特殊,它用来存储类的元信息。对于GC是否发生在永久代有许多不同的看法,在我看来这取决于采用的JVM。大家可以通过创建大量的字符串来观察是发生了GC还是抛出了OutOfMemoryError。

    GC算法
    标记清除算法
    分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。该算法的缺点是效率不高并且会产生不连续的内存碎片。
    imageimage
    复制算法
    把内存空间划为两个区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。优点:实现简单,运行高效。缺点:会浪费一定的内存。一般新生代采用这种算法。
    imageimage
    标记整理算法
    标记阶段与标记清除算法一样。但后续并不是直接对可回收的对象进行清理,而是让所有存活对象都想一端移动,然后清理。优点是不会造成内存碎片。
    imageimage
    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参数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,是自动的,无法强制执行。

    展开全文
  • 针对年轻代的垃圾回收算法,叫做复制算法 如下图,首先吧年轻代的内存分为两块: 假设有如下代码,在“loadReplicasFromDisk()”方法中创建了对象,此时对象会分配在年轻代其中一块内存空间里 而且是由“main...

    1.背景引入

    针对年轻代的垃圾回收算法,叫做复制算法

    如下图,首先吧年轻代的内存分为两块:

    假设有如下代码,在“loadReplicasFromDisk()”方法中创建了对象,此时对象会分配在年轻代其中一块内存空间里

    而且是由“main线程”的栈内存中的“loadREplicasFromDisk()”方法的栈帧内的局部变量来引用的

    public class Kafka {
        public static void main(String[] args) {
            loadReplicasFromDisk();
        }
        
        private static void loadReplicasFromDisk() {
            ReplicaManager replicaManager = new ReplicaManager();
        }
    }
    

    假设与此同时,代码在不停地运行,然后大量的对象都分配在了新生代内存的其中一块内存区域里,也只会分配在那块区域里

    而且分配过后,很快就失去了局部变量或者类静态变量的引用,成为了垃圾对象,如下图

    接着这个时候,年轻代内存那块被分配对象的内存区域基本快满了,再次分配对象的时候,发现内存空间不足了。

    此时就会出发Minor GC 去回收年轻代那块被使用的内存空间的垃圾对象。

    那么回收掉时候是怎么做的呢?

     

    2.不太好的垃圾回收思路

    假设Minor GC时,直接对内存区域中的垃圾对象进行标记 然后就直接对那块内存区域中的对象进行垃圾回收,把内存空出来

    这种思路非常不好,可能会出现下图所示的情况

    虽然回收掉了垃圾对象,保留了存活对象,但是他们在内存区域里同一个西一个,非常凌乱,造成了大量的内存碎片。

    如图红圈标记出来的,就是内存碎片,他们有的可能很大,有的可能很小。

    内存碎片过多,就会造成内存浪费

    比如现在打算分配一个新的对象,尝试在上图那块被使用的内存区域里去分配

    可能因为内存碎片太多的缘故,虽然所有的内存碎片加起来其实有很大的一块内存,但是因为这些内存是碎片式分散的,所以导致没有一款完整的足够的内存空间来分配新的对象。

     

    所以这种直接对一块内存空间回收掉垃圾对象,保留存活对象的方法,绝对是不可取的。

    因为内存碎片过多,是他最大的问题,会造成大量的内存浪费,很多内存碎片压根没法使用。

     

    3.合理的垃圾回收思路

    首先对那块在使用的内存空间标记处里面哪些对象是不能进行垃圾回收的,就是要存活的对象

    然后先把哪些存活的对象转移到另外一块空白的内存中,如下图

    先把存活对象转移到另外一块空白内存区域,可以吧这些对象都比较紧凑的排列在内存里

    然后那块被转移的内存区域,就会多出一大块连续的可用的内存空间

    此时就可以将新对象分配在那块连续内存空间里了

    这就是“复制算法”,把年轻代内存划分为两块内存区域,然后只使用其中一块内存

    待那块内存快满的时候看,就把里面的存活对象一次性转移到另外一个内存区域,保证没有内存碎片

    接着一次性回收原来那块内存区域的垃圾对象,再次空出来一块内存区域。两块内存区域就这么重复着循环使用

     

    4.复制算法缺点

    复制算法的缺点非常明显,如果按照上述的思路,假设我们给年轻代1G的内存空间,那么只有512MB的内存空间是可以用的

    另外512MB的内存空间是一直要放在那里空着的,然后512MB内存空间满了,就把存活对象转移到另外一块512MB的内存空间去

    从始至终,就只有一半的内存可以用,这样的算法显然对内存的使用效率太低了。

     

    5.复制算法的优化:Eden区和Survivor区

     

    实际上真正的复制算法会做出如下优化。把年轻代内存区域分为三块

     

    1个Eden区,2个Survivor区,其中Eden区占80%内存空间,每一块Survivor区各占10%的内存空间

    比如说Eden区有800MB内存,每一块Survivor区就100MB内存,如下图;

    平时可以使用的,就是Eden区和其中一块Survivor区,那么相当于就是有900M的内存是可以使用的。

     

    但是刚开始对象是分配在Eden区的,如果Eden区快满了,就会出发垃圾回收

    此时就会把Eden区中的存活对象一次性转移到一个空的Survivor区,接着Eden区就会被情况,然后再次分配新对象到Eden区

    如下图所示,Eden区和一块Survivor区里有对象,其中Survivor区里放的是上一次Minor GC后存活的对象

    如果下次Eden区满,那么再次出发Minor GC,就会把Eden区和放着上一次Minor GC 后存活对象的Survivor区内存的存活对象转移到另外一块Survivor区去。

     

    因为每次垃圾回收可能存活下来的对象就1%,所以设计到时候就留了一块100MB的内存空间来存放垃圾回收后转移过来的存活对象

     

    比如Eden区+一块Survivor区有900MB的内存空间都占满了,但是垃圾回收之后,可能就10MB对象是存活的。

    此时就把那10MB的存活对虾转移到另外一块Survivor区域就可以,然后再一次把Eden区和之前使用的Survivor区里的垃圾对象全部回收掉、

     

    接着新对象继续分配在Eden区和另外那块开始被使用的Survivor区,然后始终保持一块Survivor区是空着的,就这样一直循环使用这三块区域

    这么做最大的好处,就是只有10%的内存空间是被闲置的,90%的内存都被使用上了;无论是垃圾回收的性能,内存碎片的控制,还是说内存使用的效率,都非常的好。

     

     

     

    展开全文
  • jvm 年轻代、年老代、永久代

    千次阅读 2018-11-13 18:49:47
    关键字约定   Young generation –&...年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当...
  • 在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。 这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象...
  • 关于JVM,也许你听过这些术语:年轻代(新生代)、老年代、永久代、minor gc(young gc)、major gc、full gc 不要急,先上图,这是jvm 堆内存结构图 仔细的你发现了 图中有些分数8/10和1/10,这是默认配置下各个...
  • 文章目录PreGC回收器概述最常用的新生代垃圾回收器:ParNew如何为线上系统指定使用ParNew垃圾回收器?ParNew垃圾回收器默认情况下的线程数量思考面试题,parnew+cms的gc,如何保证只做ygc,jvm参数如何配置 ...
  • -Xmn 年轻代大小(1.4or lator) 注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小. 增大年轻代后,将会减小年老代大小.此值对...
  • 所以这里不用在意,当它写错了 三、并行回收GC(Parallel)(Parallel Scavenge) 默认的那个 (俗称:吞吐量优先收集器) 年轻代默认使用 和Parallel Old相互激活 使用上面任意一个后,老年代默认开启使用Parallel Old...
  • JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配 2015年03月27日 15:38:48 阅读数:2091 如果想观察JVM进程占用的堆内存,可以通过命令工具jmap或者可视化工具jvisualvm.exe。JVM这些启动参数都拥有...
  • JVM 年轻代和年老代 大小设置

    万次阅读 2017-11-22 18:29:48
    年轻代在经过n(hotspot默认是15)轮后会进入老年代, 这样老年代顶不住了,就会触发full gc, 回收时需要 stop the world ,这样系统经 常发生长时间停顿 ,影响系统的 吞吐量 ...
  • JVM所管理的内存空间中,Java堆是最大的一块,主要用于存放各类实例对象,如下图: JVM中的堆被划分为两个不同区域:新生代Young、老年代Old。...年轻代 年轻代用于存放新创建的对象,存储大小默认为堆大小的...
  • 关键字约定 Young generation –>新生代 ...年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当
  • 文章目录Pre躲过15次GC之后进入老年动态对象年龄判断大对象直接进入老年代Minor GC后的对象太多,无法放入Survivor区怎么办?老年代空间分配担保规则老年代垃圾回收算法 Pre 上篇文章已经讲清楚了新生代的垃圾...
  • 为什么要区分年轻代和老年代呢? 年轻代里存放的大多数对象是短期存活的对象,需要用复制回收算法。老年代里存放的对象是长期存活的对象,用的是标记-整理算法。所以才需要区分年轻代和老年代。 一种不太好的回收...
  • 1.为什么会有年轻代我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能。你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我们要找到...
  • 年轻代的垃圾收集器包含: Serial、ParNew、Parallell 老年代则包括Serial Old老年代版本、CMS、Parallel Old老年代版本和JDK11中的G1收集器。 Serial:单线程版本收集器,进行垃圾回收的时候会STW(Stop The World...
  • 年轻代分Eden区,Servior区。Servior区又分为From space区和To Space区。Eden区和Servior区的内存比为8:1。默认值是保持为堆的1/15.可以通过-Xmn参数设置年轻代为固定大小。 应用程序只能使用一个新生代和复活代,...
  • 年轻代(Young Generation): 所首先新生成的对象都是放在年轻代的。年轻代的目标就是尽可能快速的回收掉那些生命周期短的对象 。 年轻代分为三个区:一个Eden区,两个Survivor区(一般情况)。大部分对象是在...
  • JVM 的堆空间分成2个区域:年轻代、老年代 年轻代又进一步细分成3个区域:Eden、Survivor From、Survivor To 如下图所示: 默认情况下,年轻代与老年代比例为1:2。可以通过参数-XX:NewRatio修改,NewRatio默认值是...
  • Full GC会不会回收年轻代讨论

    千次阅读 2016-08-04 11:39:41
    在1.6 update24之前,在发生minor GC前虚拟机会检查老年代最大的可用的连续空间是否大于年轻代所有对象的总和,弱国这个条件成立,则MinorGC是安全的。如果不成立,则虚拟机查看HandlePronotionFailure是否设置允许...
  • JVM内存:年轻代、老年代、永久代

    千次阅读 2018-03-25 21:10:35
    参考文章:JVM内存:年轻代,老年代,永久代 Java中堆是JVM所管理的最大的一块内存空间,主要用于存放各种类的实例对象和数组,如下图所示: 在Java中,堆被划分成两个不同的区域:年轻代、老年代。年轻代...
  • Java中什么情况产生年轻代内存溢出、什么情况产生年老代内存溢出 谢谢各位大佬指教
  • JVM内存中的年轻代,老年代

    千次阅读 2018-07-25 00:02:45
    引言 对于大多数Java应用来说,Java Heap(Java堆)是JVM管理的内存中较大的一块,而且Java Heap是被所有...再从内存回收的角度来看,由于现代收集器基本都采用分收集算法,所以Java Heap还可以被细分为: 新生...
  • 这个问题考察了很多知识点,我们需要对Java内存管理和垃圾回收机制有很深入的认识,本文将系统阐述这两个方面内容,最后再给出这个问题的答案。该问题是我面试国内一家主攻云安全的互联网公司的问题之一,这也说明了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 87,423
精华内容 34,969
关键字:

年轻代