Java 内存垃圾回收

基本概念 


    JVM垃圾回收:不同于C++,java可以自动进行垃圾回收(也可以调用System.gc(),但是不保证一定处罚垃圾回收)。    

    内存溢出:内存对象的生命周期超过了程序所期望的存活时间。

    垃圾回收的基本步骤:

  • 发现无用对象

  • 回收无用对象所占用的内存空间


GC ROOT

  • 虚拟机栈中引用的对象(本地变量表)

  • 方法区中静态属性引用的对象

  • 方法区中常量引用的对象

  • 本地方法栈中引用的对象(Native对象)


垃圾回收算法    

  1. 引用计数方法。当一个对象实例被创建时,分配一个变量进行计数,任何变量引用这个对象,则+1,任何变量失去对象的引用(变量超过了生命周期或指向其他对象实例),则-1。对象实例的引用为0,则视为垃圾。无法检测循环引用。

  2. 标记-清除算法。从GC ROOTS集合进行扫描,对所有到达的对象实例标记为存活状态,未被标记的对象则视为垃圾。标记-清除算法直接回收不存活对象,会产生内存碎片。

  3. 标记-整理算法。对标记-清除算法进行改进,以避免产生内存碎片。在清理垃圾对象时,会同时进行移动操作(移动和增加句柄和句柄表,会产生额外成本)。

  4. 复制算法。克服句柄开销和解决堆碎片。把堆分成一个对象面和多个空闲面。对象实例的创建都在对象面进行,当需要进行垃圾回收时,从GC ROOT集合开始扫描活动对象,并将活动对象复制到空闲面。结束之后,空闲面变成了对象面,而对象面成了空闲面。

  5. 分代算法。不同的对象生命周期是不一样的。


年轻

    包括eden、survivor0和survivor1,比例一般是8:1:1。回收步骤,

    1、eden区的存活对象都复制到survivor0,然后清空eden区。当survivor0满了,则将eden区和survivor0区存活对象复制到survivor1区。清空eden和survivor0。并设置survivor1为survivor0,而survivor1为survivor0.

    2、当survivor1满了,则将存活对象直接复制到老年代。

    3、当老年代满了,则处罚full gc


年老代

    内存比新生代也大很多(大概比例是1:2)


持久代

    用于存放静态文件,如Java类、方法


垃圾收集器

    新生代:Serial,PraNew,Parallel Scavenge

    老年代:Serial Old,Parallel Old, CMS

  • Serial收集器(复制算法)。新生代单线程收集器。JAVA虚拟机运行在Client模式下的默认新生代收集器。在运行过程中会stop the world,将所有线程都暂停。简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

  • Serial Old(标记-整理算法)。老年代单线程收集器。主要意义也是被Client模式下的虚拟机使用。如果在Server模式下,它主要还有两大用途:一个是在JDK 1.5及之前的版本中与Parallel Scavenge收集器搭配使用,另外一个就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure的时候使用。

  • ParNew收集器(停止-复制算法)。Serial收集器的多线程版本。除了Serial收集器外,目前只有它能与CMS收集器配合工作

  • Parallel Scavenge收集器(停止-复制算法)。并行收集器,追求高吞吐量,高效利用CPU。吞吐量一般为99%, 吞吐量= 用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互相应要求不高的场景。Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数及直接设置吞吐量大小的 -XX:GCTimeRatio参数。根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量。自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。

  • Parallel Old收集器(停止-复制算法)。Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先。

  • CMS(Concurrent Mark Sweep)收集器(标记-清理算法)。并发低停顿收集器。以获取最短回收停顿时间为目标的收集器。会产生碎片,可以设置不同的参数尽量避免碎片

  • G1(Garbage First)收集器(标记-整理算法)。一是G1收集器是基于“标记-整理”算法实现的收集器,也就是说它不会产生空间碎片,这对于长时间运行的应用系统来说非常重要。二是它可以非常精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。收集周期:初始标记(CMS-initial-mark) -> 并发标记(CMS-concurrent-mark) -> 重新标记(CMS-remark) -> 并发清除(CMS-concurrent-sweep) ->并发重设状态等待下次CMS的触发(CMS-concurrent-reset)。

            


工程配置

    

-XX:ParallelCMSThreads=4

-XX:+CMSParallelRemarkEnabled

-XX:+ExplicitGCInvokesConcurrent

-XX:+CMSPermGenSweepingEnabled

-XX:+CMSClassUnloadingEnabled

-XX:+TieredCompilation

-XX:CICompilerCount=4

 

ExplicitGCInvokesConcurrent:打开此参数后,在做System.gc()时会做background模式CMS GC,即并行FULL GC,可提高FULL GC效率。注,该参数在允许systemGC且使用CMS GC时有效。主要解决的是:1、使用了NIO或者NIO框架(Mina/Netty等)2、使用了DirectByteBuffer分配字节缓冲区 3、使用了MappedByteBuffer做内存映射。问题是:nio框架中频繁调用System.gc会严重影响性能。

CMSParallelRemarkEnabled:降低标记停顿

ExplicitGCInvokesConcurrent:只有在CMS时有效,改变System.gc()的默认行为,使System.gc()还是会触发GC时,不进行完全stop-the-world的full GC,而是进行并发GC。

CMSClassUnloadingEnabled:允许对类进行卸载。防止出现类定义一直存活。glib产生的动态类、Sping和Hibernate代理的类

CMSPermGenSweepingEnabled :允许对持久代进行清理。java6被移除。

TieredCompilation分层编译。在系统执行初期,执行频率比较高的代码先被c1编译器编译,以便尽快进入编译执行,然后随着时间的推移,执行频率较高的代码再被c2编译器编译,以达到最高的性能。

CICompilerCount:调整编译线程的数目

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值