GC 垃圾回收

GC基本原理

GC(Garbage Collection),是JAVA/.NET中的垃圾收集器。

Java是由C++发展来的,它摈弃了C++中一些繁琐容易出错的东西,引入了计数器的概念,其中有一条就是这个GC机制(C#借鉴了JAVA)

编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。所以,Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放。

对于程序员来说,分配对象使用new关键字;释放对象时,只要将对象所有引用赋值为null,让程序不能够再访问到这个对象,我们称该对象为”不可达的”.GC将负责回收所有”不可达”对象的内存空间。

对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是”可达的”,哪些对象是”不可达的”.当GC确定一些对象为”不可达”时,GC就有责任回收这些内存空间。但是,为了保证 GC能够在不同平台实现的问题,Java规范对GC的很多行为都没有进行严格的规定。例如,对于采用什么类型的回收算法、什么时候进行回收等重要问题都没有明确的规定。因此,不同的JVM的实现者往往有不同的实现算法。这也给Java程序员的开发带来行多不确定性。本文研究了几个与GC工作相关的问题,努力减少这种不确定性给Java程序带来的负面影响


java 内存结构由堆,栈 ,本地方法栈,方法区等部分组成 ,JVM 分别堆新生代和旧生代采用不同的垃圾回收机制。
堆:
所有通过NEW 创建的对象的内存都在对中分配,七大小可以通过-Xms 和-Xmx 指定,堆被划分为新生代和旧生代,新生代又分为eden 和survivor区,最后survivor由 formspace 和tospace组成。当eden 空间不足时,会把存活的对象转移到survivor 中,新生代的大小可以通过 -Xmn 控制,也可以用 –XX:SurvivorRatio来控制eden和survivor 的比例,用于存放新生代中经过多次垃圾回收任然存活的对象
栈:
每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧存放局部变量和操作数栈,用于存放方法调用过程中的临时变量,参数和中间结果。
方法区:
存放了要加载的类,静态变量,final类型的常量属性发方法信息,jvm用持久代来存放方法区,可通过–XX:PermSize –XX:MaxPermSize来指定最小值和最大值,

对新生代的GC:
新生代通常存活时间比较短,因此基于copying 算法来进行垃圾回收,copying 算法指扫描出存活对象,并复制到一块引得完全没有使用的空间中,对于新生到,看就是在eden 和formspace 或tospace之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持在最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC,当连续分配对象时,就会逐渐从eden到survivor,最后到就旧生代,当旧生代也填满时就回报outOfMemory

新生代GC在执行机制上JVM 提供了3种GC方式
串行GC(SerialGC)
并行回收GC(ParallelScavenger)
并行GC
1) 串行GC
在扫描和复制过程中采用单线程的方式来进行,适用于单CPU,新生代空间较小以及对展厅时间要求不是非常高的应用上,是client级别的默认垃圾回收方式,可以通过–XX:+userSerialGC 来控制指定
2)并行回收GC
在扫描和复制过程中采用多线程的方式来惊醒,适用于多CPU,对暂停时间要求较短的应用上,,是server级别默认采用的垃圾回收方法,可以用–XX:UserParallelGC 来强制指定,用–XX:ParallelGCThreads=4来指定线程数
3)并行GC
与旧生代的冰法GC配合使用

旧生代的GC:
旧生代的GC,由于对象存活时间比较长,比较文稳定,因此采用(mark)标记清除算法来进行回收,所谓标记就是扫描出存活的对象,然后在进行回收违背标记的对象,回收后对用空出的空间要么进行合并要么标记出来用于下次分配,目的就是减少内存碎片带来的小路损耗。
旧生代的执行机制提供3种方式:
1)串行GC(SerialMSC)
2)并行GC(parallelMSC)
3)并发GC(CMS)

JVM内存申请过程如下:

JVM 会试图为相关Java对象在Eden中初始化一块内存区域
当Eden空间足够时,内存申请结束;否则到下一步
JVM 试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区
Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区
当OLD区空间不够时,JVM 会在OLD区进行完全的垃圾收集(0级)
完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory”错误

增量式GC

增量式GC(Incremental GC),是GC在JVM中通常是由一个或一组进程来实现的,它本身也和用户程序一样占用heap空间,运行时也占用CPU。

当GC进程运行时,应用程序停止运行。因此,当GC运行时间较长时,用户能够感到Java程序的停顿,另外一方面,如果GC运行时间太短,则可能对象回收率太低,这意味着还有很多应该回收的对象没有被回收,仍然占用大量内存。因此,在设计GC的时候,就必须在停顿时间和回收率之间进行权衡。一个好的GC实现允许用户定义自己所需要的设置,例如有些内存有限的设备,对内存的使用量非常敏感,希望GC能够准确的回收内存,它并不在意程序速度的快慢。另外一些实时网络游戏,就不能够允许程序有长时间的中断。

增量式GC就是通过一定的回收算法,把一个长时间的中断,划分为很多个小的中断,通过这种方式减少GC对用户程序的影响。虽然,增量式GC在整体性能上可能不如普通GC的效率高,但是它能够减少程序的最长停顿时间。

Sun JDK提供的HotSpot JVM就能支持增量式GC。HotSpot JVM缺省GC方式为不使用增量GC,为了启动增量GC,我们必须在运行Java程序时增加-Xincgc的参数。

HotSpot JVM增量式GC的实现是采用Train GC算法,它的基本想法就是:将堆中的所有对象按照创建和使用情况进行分组(分层),将使用频繁高和具有相关性的对象放在一队中,随着程序的运行,不断对组进行调整。当GC运行时,它总是先回收最老的(最近很少访问的)的对象,如果整组都为可回收对象,GC将整组回收。这样,每次GC运行只回收一定比例的不可达对象,保证程序的顺畅运行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值