GC机制详解

什么是GC

垃圾回收机制是由垃圾收集器(Garbage Collection,即GC)来实现的,GC是后台的守护进程。它的特别之处在于它是一个低优先级进程,但是可以根据内存的使用情况动态调整优先级。

Java语言中一个显著的特点就是引入了垃圾回收机制,使c/c++程序员最头疼的内存管理问题迎刃而解,它使得Java程序员在编写程序的时候不用考虑内存管理,JVM替我们完成了这部分工作。

垃圾回收的对象

1.对非线程的对象来说,当所有的活动线程都不能访问该对象,则该对象变为垃圾。

2.对线程对象来说,满足上面的条件,且线程未启动或者已停止。

程序中的不可用对象(不存活的对象,没有任何引用),或者无用的变量信息等,在程序中长期存在会逐渐占用较多的内存空间,导致没有足够的空间分配给新生成的对象等.

早期jdk使用引用计数法,计数每个对象的引用次数,对于没有引用的对象进行删除,但是该方法无法处理循环引用情况.

之后引入了可达性分析算法,将所有的引用关系看作一张图,从一个结点(GC ROOT)开始,寻找向下的引用结点,之后重复这一过程,其中的路径被称为引用链,当一个对象没有与GC ROOT相连的引用链时,该对象是不可用的

GC的内存区域

jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。

触发GC时间

时间不确定。

垃圾回收机制是由垃圾收集器(Garbage Collection,即GC)来实现的,GC是后台的守护进程。它的特别之处在于它是一个低优先级进程,但是可以根据内存的使用情况动态调整优先级。因此,GC会在内存中低到一定限度时才自动运行,从而实现对内存的回收。这就是垃圾回收的时间不确定的原因。

为何要这样设计?因为GC也是进程,也要消耗CPU等资源,如果执行过于频繁,会对java程序的执行产生较大的影响(java解释器本来就不快),因此JVM的设计者们选择了不定期的进行垃圾回收。

触发方法

(1)程序调用System.gc时可以触发

(2)系统自身来决定GC触发的时机(根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动GC线程并停止应用线程)

GC又分为 minor GC 和 Full GC (也称为 Major GC )

Minor GC触发条件:当Eden区满时,触发Minor GC。

Full GC触发条件:

a.调用System.gc时,系统建议执行Full GC,但是不必然执行

b.老年代空间不足

c.方法区空间不足

d.通过Minor GC后进入老年代的平均大小大于老年代的可用内存

e.由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

执行回收的时间与算法

在CPU空闲时自动回收,或在堆内存满后进行回收,或者程序中调用System.gc()后进行回收

  执行回收的算法共有四种

  1. 标记-清除算法

  分两步执行,首先标记,根据可达性分析标记出所有需要回收的对象,之后对被标记的对象进行回收.

  该方法不需要移动对象,只对不可用对象操作,较为简单.但是一般情况下,效率较低,同时由于直接回收垃圾,会产生内存碎片,后续为较大对象分配空间时,可能因为无法找到较大的连续内存空间而必须再次进行垃圾回收过程

  2. 复制算法

  将可用的内存按照容量大小等分,每次只使用一块空间进行分配,当这一块用完时,将可用对象移动到另一块空间上,然后清理已经用过的空间.

  该方法每次只需要对一半的空间进行回收,同时解决了内存碎片的问题.但是使用中浪费了一半的内存空间,在可用对象较多的情况下需要进行较多的复制,效率降低.

  3. 标记-整理算法

  标记-清除法的改进.在使用标记-清除算法回收不可用对象后,将所有可用的对象压缩到内存的一端,之后清理端边界之外的所有内存.

  该方法解决了内存碎片的问题,但是增加了对象移动的过程,执行的成本较高

  4. 分代收集算法

  目前主要使用的方法

  根据对象存活的周期将内存(堆)分为多块,一般是新生代,老年代和永生代(永久代).在不同的代使用不同的收集器(收集器使用不同的算法)进行回收,提高效率.

分代回收

将堆区分为3个区域:

新生代(Young Generation)、老年代(Old Generation)、永久代(Permanent Generation)

这三个代里面,永生代则对应JVM中的方法区,存放的是类的结构信息,以及运行时常量池之类;

而新生代和老年代分别用于存放不同时期的对象或大小不一的对象。这里面有一系列算法。简而言之,就像人的生命周期一样,新创建的对象放在新生代(比较大也可以直接放到老年代),而新生代又分为伊甸区和两个存活区,对象会在这些区域之间搬来搬去。

经过几轮回收淘汰以后,移到老年代。

新生代:采用停止-复制算法。停止(Stop-the-world)”的意义是在回收内存时,需要暂停其他所 有线程的执行。

老年代:采用标记-整理算法,即:标记出仍然存活的对象(存在引用的),将所有存活的对象向一端移动,以保证内存的连续。

永久代:不一定需要回收。

Stop The World 、OopMap、安全点

进行垃圾回收的过程中,会涉及对象的移动。为了保证对象引用更新的正确性,必须暂停所有的用户线程,像这样的停顿,虚拟机设计者形象描述为「Stop The World」。也简称为STW。

在HotSpot中,有个数据结构(映射表)称为「OopMap」。一旦类加载动作完成的时候,

HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来,记录到OopMap。在即时编译过程中,也会在「特定的位置」生成 OopMap,记录下栈上和寄存器里哪些位置是引用。这些位置就叫作「安全点(safepoint)。」 用户程序执行时并非在代码指令流的任意位置都能够在

停顿下来开始垃圾收集,而是必须是执行到安全点才能够暂停。

HotSpot是一款Java虚拟机。

使用技巧

1.尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为 null.我们在使用这种方式时候,必须特别注意一些复杂的对象图,例如数组,队列,树,图等,这些对象之间有相互引用关系较为复杂。对于这类对象,GC 回收它们一般效率较低。如果程序允许,尽早将不用的引用对象赋为null.这样可以加速GC的工作。

2.尽量少用finalize函数。finalize函数是Java提供给程序员一个释放对象或资源的机会。但是,它会加大GC的工作量,因此尽量少采用finalize方式回收资源。

3.如果需要使用经常使用的图片,可以使用soft应用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起OutOfMemory.

4.注意集合数据类型,包括数组,树,图,链表等数据结构,这些数据结构对GC来说,回收更为复杂。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费。

5.当程序有一定的等待时间,程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。使用增量式GC可以缩短Java程序的暂停时间。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值