欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JVM垃圾回收知识汇总 博客分类: java qu

程序员文章站 2024-02-18 23:54:04
...

什么是垃圾回收

  • java程序运行期间生成新对象,加载类文件都需要占用内存,不同对象从被创建,使用,存储到最后使用完毕被销毁都有一个完整的生命周期,java程序开发人员实际上只需要负责对象的创建使用,对象的销毁回收由虚拟机自动完成,对象的回收过程就是垃圾回收

怎么来做垃圾回收

  • jvm运行期间使用的内存包括堆内存,创建线程使用的vm栈内存,存储方法,类,常量池使用的永久代内存,堆外内存(direct buffer),开发人员使用的是heap(堆内存),也是重点需要管理的区域
  • 由于不同对想的生命周期长短不同,因此需要将整块堆内存分成不同的区域,E,S1,S2,O,Eden区存放生命周期最短的对象,S区中等长度,Old区存长生命周期对象,因此对不同的区域启用不同的回收算法。
  • 对象在堆的各个区域移动过程如下:E区满了进行一轮回收,剩下的晋升到空的S区,同时将非空的S区进行回收,剩下的也全部拷贝到空S区,将达到存活时间的对象晋升进入O区。
  • 为什么会有两个S区,主要是为了处理回收碎片,E区满了回收之后所有对象拷贝进入S区,S区也会被回收,因此S区面临三种选择:1)整理并压缩碎片,空出整块的内存区域,2)将E区晋升上来的对象适配到S区中的碎片空间中;3)设置两块S区,将E区和非空S区的存活对象全部合并拷贝进入空S区;最终SUN选择第三种方法,从直觉上考虑直接合并拷贝应该是效率最高的,相当于用空间换时间 

垃圾回收参数

其中 -Xmx 控制了 E S1 S2 O 这四个区的总大小,-XX:PermSize 和 -XX:MaxPermSize控制了PermGen的大小,两块的大小独立设置,因此总的堆内存大小 E+S1+S2+O+P= -Xmx + -XX:MaxPermSize

 

-Xms 初始堆大小,MinHeapFreeRatio参数可以调整空闲堆内存小于40%时候JVM会扩充堆内存到最大设置值

-Xmx 最大堆大小,MaxHeapFreeRatio参数可以控制空闲堆内存大于70%时候JVM减少堆知道最小值

-Xmn 新生代大小(E + S1 +S2),SUN推荐的大小是整个堆大小的3/8,增大这个值会减小O区的大小

-XX:PermSize 初始永久带大小

-XX:MaxPermSize 最大永久带大小,这个值设置推荐是系统P区基本稳定下来的量的1.5倍左右

-XX:SurviorRatio E区和S区的比例,默认是8,

-Xss: 每个线程的堆栈大小,jdk1.5之前是256,之后是1M。在物理内存不变的情况下减小这个值能生成更多的线程。一个进程内的线程最大数经验值推荐在3000-5000左右,调用栈不深的应用128K差不多够用了,大一点的差不多256K,具体的值还需要严格的测试。

 

常见的垃圾回收器

Serial回收器:jvm历史最悠久的回收器,jdk在client模式下默认的回收器,特点是启动回收的时候需要停止一切用户线程,对于小内存的应用回收效率高,不适合对响应时间要求高,大内存的应用;

Parallel回收器:并行回收相当于Serial串行回收器的多线程版,回收的时候还是需要stop the word,只是启用多线程回收让停顿时间缩短,满足对用户响应时间比较看重的应用,比如WEB应用。

CMS回收:也叫并发回收,针对Old区的回收分成6个阶段:

初始标记(CMS initial mark)

并发标记(CMS concurrent mark)

并发预清理(CMS-concurrent-preclean)

重新标记(CMS remark)

并发清除(CMS concurrent sweep)

并发重置(CMS-concurrent-reset)

初始标记和重新标记需要S-T-W,其他步骤都是和用户线程一起运行,初始标记只是把从GC Root能直接关联到的对象快速标记出来,重新标记标记是对在初始标记之后新产生的可回收对象补充做一轮标记,并发标记是GC Root Tracing,花的时间比较长。

 

CMS的young区默认使用ParNew的并发回收算法,也不会S-T-W,CMS优点众多,把JVM停顿的时间竟可能缩短,也有几个缺点:1)在回收的过程中用户线程继续运行的方式可能会发生回收还没做完但是没足够空间满足应用对内存的申请或者没有足够空间放下新晋升到Old区的对象,导致 发生 conncurrent mode failed,触发一次串行的FGC,大内存的FGC停顿时间会相当长,比较要命,因此要妥善设置CMS触发阀值,和Old区的大小,提前预留足够的Old区空间;2)同用户线程一块运行会额外占用一些CPU资源;3)产生内存碎片,启用碎片压缩的情况下在FGC事后会进行碎片整理

 

DirectBuffer的垃圾回收

  • 堆外内存的好处是减少数据拷贝次数,减少对heap堆垃圾回收的影响
  • java中的类DirectBuffer只是个堆外内存的引用,存储了实际内存引用地址和大小,封装了申请和回收的操作,内存的申请和回收实际上是调用native方法类似 malloc和calloc的方法;
  • DirectBuffer本身的对象大小很小,但是关联的堆外内存可能非常大,因此使用它的风险在于如果没好好回收DirectBuffer,明明堆内还很空,但是整个系统内存可能已经被耗尽了,因此JVM使用XX:MaxDirectMemorySize来约束申请的堆外内存量,防止由于使用不当,一个java进程搞挂整台机器
  • DirectBuffer的回收机制也很特殊,因为DirectBuffer这个java对象和关联的堆外内存实际上是处于两块不同区域,它通过关联一个PhantomReference,当DirectBuffer对象没有外部强引用之后其本身会被塞进一个ReferenceQueue,这个特殊实现的Queue里头有个后台线程一直扫描队列,把队列中的对象所关联的堆外内存释放掉,这种机制的好处是JVM对于内存的管理统一了,堆外这种异类就用异步的方式来特殊实现来回收释放;
  • 堆外内存在申请的时候如果不够就会调用System.gc触发FGC,并且为了及时回收堆外内存也会经常手工调用System.gc,因此设置这个参数也很重要-XX:+ExplicitGCInvokesConcurrent,把System.gc转换成一次CMS的执行,提高效率

垃圾回收日志格式

基本上都是这种格式:回收前区域占用的大小->回收后区域占用的大小(区域设置的大小),占用的时间,

具体的网上找下文档

 

垃圾回收调优处理过程--观察

  1. 收集当前java进程的内存相关启动参数,观察系统日志有没有OOM异常,观察GC日志,关注FGC以及concurrent mode failed 或者 promotion failed异常,以及GC监测工具观察系统内存使用状况;
  2. minor gc执行频率,如果执行非常频繁就要关注内存大小是否够用,程序代码写的质量是否不高;
  3. FGC执行频率,每次FGC回收效果如何,这个现象要考察内存大小是否够用,如果每次FGC效果差需要重点怀疑内存泄露;
  4.  minor gc 和 survior回收效果如何,每次会有多少对象晋升到老年代,如果回收效果差,要考察系统使用内存情况(heap dump)或者Young区大小是否够用,系统是频繁产生大对象;
  5. CMS频率是否执行过高,对于这一条可以检查下触发CMS的所有条件,包括:1)old区占用量是否导到达触发CMS阀值;2)是否开启了JVM根据成本计算自主决定CMS是否启用;3)永久带回收是否打开,并且永久带占用是否达到回收阀值;4)是否设置了ExplicitGCInvokesConcurrent,由外部system.gc触发

垃圾回收调优处理过程--设定调优目标

根据第一步观察到的状况来设定调优目标:

  1. OOM--调大内存占用,MAT分析内存详情,排除内存泄露
  2. 降低minor gc 频率活minor gc执行时间
  3. 降低 fgc 频率或 fgc 执行时间

调整的效果可以通过 jstat 的 gcutil来观察调优目标是否达到

 

垃圾回收调优处理过程--常用策略的选择

  1. 降低FGC频率的办法:1)加大内存以及Old区;2)观察每次survior回收后是否大量晋升到Old,可以尝试调大S区,或者调大S区存活次数;3)调优程序,控制缓存等长生命周期对象的使用量
  2. 降低minor gc频率的办法:1)加大Eden区;2)用CMS并发回收器;3)增加并行收集的线程数;4)升级CPU;
  3. 降低minor gc回收时间或old 回收时间:1)减小区域占用内存大小;2)对于CMS,调小CMS触发阀值;3)换更多更牛逼的CPU;4)优化代码

 JDK 7,JDK8中的新特性:G1垃圾回收器

 G1 GC是jdk 7 对JVM GC算法做了一次重大升级,当初计划用来替换CMS。JDK 8中进一步对G1做了优化,去掉了永久带。G1的目标是减少因为GC带了的系统停顿。

G1将Java堆内存空间划分成一块块等大的区域(region),每个区域都是一段连续的内存空间,E,S,O区将不再固定,而是可以利用任意的空白区块。小区快带来的好处是后台回收线程在扫描可回收对象时候可以优先清理那些包含更多可回收对象的区块,用相同的停顿时间回收更多的垃圾,总体提高回收效率,缩短总体停顿时间。和CMS比有下面一些优势:

  • G1通过将内存空间分成区域(Region)的方式避免内存碎片问题
  •  Eden, Survivor, Old区不再固定、在内存使用效率上来说更灵活
  • 可以预设停顿时间控制GC停顿对系统响应的影响程度
  • G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW(stop the world)的时候做
  • G1会在Young GC中使用、而CMS只能在O区使用

 

 

 

相关标签: qu