java垃圾回收机制

Java垃圾回收机制

为什么要进行垃圾回收?

    垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。

    注意:垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身。换言之,垃圾回收只会负责释放那些对象占有的内存。对象是个抽象的词,包括引用和其占据的内存空间。当对象没有任何引用时其占据的内存空间随即被收回备用,此时对象也就被销毁。但不能说是回收对象,可以理解为一种文字游戏。

 

怎么定义垃圾

既然我们要做垃圾回收,首先我们得搞清楚垃圾的定义是什么,哪些内存是需要回收的。

分析:

    引用:如果Reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。(引用都有哪些?对垃圾回收又有什么影响?

    垃圾:无任何对象引用的对象(怎么通过算法找到这些对象呢?)。

    回收:清理“垃圾”占用的内存空间而非对象本身(怎么通过算法实现回收呢?)。

    发生地点:一般发生在堆内存中,因为大部分的对象都储存在堆内存中(堆内存为了配合垃圾回收有什么不同区域划分,各区域有什么不同?)。

    发生时间:程序空闲时间不定时回收(回收的执行机制是什么?是否可以通过显示调用函数的方式来确定的进行回收过程?

    带着这些问题我们开始进一步的分析。

2.Java中的对象引用

  (1)强引用(Strong Reference):如“Object obj = new Object()”,这类引用是Java程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。

  (2)软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2之后提供了SoftReference类来实现软引用。

  (3)弱引用(Weak Reference):它也是用来描述非须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。

  (4)虚引用(Phantom Reference):最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。JDK1.2之后提供了PhantomReference类来实现虚引用。

 

3.判断对象是否是垃圾的算法。

      Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:(1)找到所有存活对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用。

3.1引用计数算法(Reference Counting Collector)

    堆中每个对象(不是引用)都有一个引用计数器。当一个对象被创建并初始化赋值后,该变量计数设置为1。每当有一个地方引用它时,计数器值就加1(a = b, b被引用,则b引用的对象计数+1)。当引用失效时(一个对象的某个引用超过了生命周期(出作用域后)或者被设置为一个新值时),计数器值就减1。任何引用计数为0的对象可以被当作垃圾收集。当一个对象被垃圾收集时,它引用的任何对象计数减1。

    优点:引用计数收集器执行简单,判定效率高,交织在程序运行中。对程序不被长时间打断的实时环境比较有利(OC的内存管理使用该算法)。

    缺点: 难以检测出对象之间的循环引用。同时,引用计数器增加了程序执行的开销。所以Java语言并没有选择这种算法进行垃圾回收。

    早期的JVM使用引用计数,现在大多数JVM采用对象引用遍历(根搜索算法)。

3.2根搜索算法(Tracing Collector)

首先了解一个概念:根集(Root Set)

    所谓根集(Root Set)就是正在执行的Java程序可以访问的引用变量(注意:不是对象)的集合(包括局部变量、参数、类变量),程序可以使用引用变量访问对象的属性和调用对象的方法。

    这种算法的基本思路:

 (1)通过一系列名为“GC Roots”的对象作为起始点,寻找对应的引用节点。

(2)找到这些引用节点后,从这些节点开始向下继续寻找它们的引用节点。

 (3)重复(2)。

 (4)搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,就证明此对象是不可用的。

    Java和C#中都是采用根搜索算法来判定对象是否存活的。

分区算法
其主要就是将整个内存分为N个多小的独立空间,每个小空间都可以独立使用,这样细粒度的控制一次回收都少个小空间和那些个小空间,而不是对整个空间进行GC,从而提升性能,并减少GC的停顿时间。

GC算法优劣标准
评价一个垃圾收集GC算法的两个标准

吞吐量(throughput)越高算法越好
暂停时间(pause times)越短算法越好
吞吐量
JVM在专门的线程[GC Threads]中执行GC 只要GC线程是活动的 就会和应用程序线程[Application Threads]争用当前可用CPU的时钟周期而吞吐量就是指应用程序线程占程序总用时的比例

停顿
一个时间段内应用程序线程让GC线程执行而完全暂停。

垃圾回收器的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器可以高效的执行,大部分情况下,会要求系统进入一个停顿的状态。停顿的目的是终止所有应用线程,只有这样系统才不会有新的垃圾产生,同时停顿保证了系统状态在某一个瞬间的一致性,也有益于更好地标记垃圾对象,因此垃圾回收时,都会产生应用程序的停顿

垃圾收集器
前言
HotSpot JVM收集器

串行收集器
概念
使用单线程进行垃圾回收的收集器,每次回收时,串行收集器只有一个工作线程,对于并行能力较弱的计算机来说,串行收集器的专注性和独占性往往有更好的性能表现。串行收集器可以在新生代和老年代中使用,根据作用于不同的堆空间,分为新生代串行收集器和老年代收集器。

-XX:+UseSerialGC :年轻串行(Serial),老年串行(Serial Old)

Serial收集器:
Serial收集器是一个新生代收集器,单线程执行,使用复制算法。它在进行垃圾收集时,必须暂停其他所有的工作线程(用户线程)。是Jvm client模式下默认的新生代收集器。对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。


Serial Old收集器
1、Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。
2、主要意义也是在于给Client模式下的虚拟机使用。
3、如果在Server模式下,那么它主要还有两大用途:
一种用途是在JDK 1.5以及之前的版本中与Parallel Scavenge收集器搭配使用[1],
另一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。


并行收集器
概念
ParNew收集器
1、Serial收集器的多线程版本
2、单CPU不如Serial,因为存在线程交互的开销

-XX:+UseParNewGC 新生代并行(ParNew),老年代串行(Serial Old)

-XX:ParallelGCThreads=n 设置并行收集器收集时使用的CPU数。并行收集线程数。一般最好和计算机的CPU相当

Parallel Scavenge收集器
-XX:+UseParallelGC 新生代使用并行回收收集器,老年代使用串行收集器

1、吞吐量优先”收集器
2、新生代收集器,复制算法,并行的多线程收集器
3、目标是达到一个可控制的吞吐量(Throughput)。
4、吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
5、两个参数用于精确控制吞吐量:
-XX:MaxGCPauseMillis 是控制最大垃圾收集停顿时间

-XX:GCTimeRatio 直接设置吞吐量大小

-XX:+UseAdaptiveSizePolicy 动态设置新生代大小、Eden与Survivor区的比例、晋升老年代对象年龄

6、并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
7、并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。

Parallel Old收集器
-XX:+UseParallelOldGC 新生代和老年代都使用并行回收收集器

1、Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
2、在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。


CMS收集器
1、以获取最短回收停顿时间为目标的收集器。
2、非常符合互联网站或者B/S系统的服务端上,重视服务的响应速度,希望系统停顿时间最短的应用
3、基于“标记—清除”算法实现的
4、CMS收集器的内存回收过程是与用户线程一起并发执行的
5、它的运作过程分为4个步骤,包括:

初始标记,“Stop The World”,只是标记一下GC Roots能直接关联到的对象,速度很快
并发标记,并发标记阶段就是进行GC RootsTracing的过程
重新标记,Stop The World”,是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,但远比并发标记的时间短
并发清除(CMS concurrent sweep)
6、优点:并发收集、低停顿
7、缺点:

对CPU资源非常敏感。
无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。
一款基于“标记—清除”算法实现的收集器
-XX:+UseConcMarkSweepGC 应用CMS收集器

-XX:ConcGCThreads 设置并发线程数量

-XX:CMSInitiatingOccupancyFraction 设置当老年代空间实用率达到百分比值时进行一次cms回收,默认为68,当老年代的空间使用率达到68%的时候,会执行CMS回收

如果内存使用率增长的很快,在CMS执行的过程中,已经出现了内存不足的情况,此时CMS回收就会失败,虚拟机将启动老年代串行回收器进行垃圾回收,这回导致应用程序中断,直到垃圾回收完成后才会正常工作,这个过程GC的停顿时间可能较长,所以该值需要根据实际情况设置。

-XX:+UseCMSCompactAtFullCollection 设置cms在垃圾收集完成后进行一次内存碎片整理

-XX:CMSFullGCsBeforeCompaction 设定进行多少次cms回收后,进行一次内存压缩。

G1(Garbage-First)收集器
1、当今收集器技术发展的最前沿成果之一
2、G1是一款面向服务端应用的垃圾收集器。
3、优点:

并行与并发:充分利用多CPU、多核环境下的硬件优势
分代收集:不需要其他收集器配合就能独立管理整个GC堆
空间整合:“标记—整理”算法实现的收集器,局部上基于“复制”算法不会产生内存空间碎片
可预测的停顿:能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒
4、G1收集器的运作大致可划分为以下几个步骤:

初始标记:标记一下GC Roots能直接关联到的对象,需要停顿线程,但耗时很短
并发标记:是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行
最终标记:修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录
筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划
-XX:+UserG1Gc 应用G1收集器

-XX:MaxGCPauseMillis 指定最大停顿时间

-XX:ParallelGCThreads 设置并行回收的线程数量

总结
在众多的垃圾回收器中,没有最好的,只有最适合应用的回收器,根据应用软件的特性以及硬件平台的特点,选择不同的垃圾回收器,才能有效的提高系统性能。

Minor GC 和 Full GC 有什么区别?
新生代 GC (Minor GC) :发生在新生代的垃圾收集动作。Minor GC 非常频繁,回收速度比较快。

老年代 GC (Major GC/Full GC):发生在老年代的 GC, Major GC 一般比 Minor GC 慢 10 倍以上。

内存分配规则

Old 区

老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记——整理算法。

除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。

★ 大对象

大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。

★ 长期存活对象

虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中每经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。

★ 动态对象年龄

虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的总合大于 Survivor 空间的一半,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。

这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值