首页 > 编程知识 正文

查看jvm内存对象,jvm垃圾回收算法有哪些

时间:2023-05-03 22:00:30 阅读:23710 作者:3466

JVM内存回收机制

标签: JVM GC垃圾回收的内存管理

0 .说明

如果用JVM创建的对象出现内存不足,JVM会自动启动垃圾回收garbagecollecting(GC )操作,直接清理不再使用但仍留在JVM内存中的对象,使其像垃圾一样

那么,要使系统能够实现不自动引用的对象回收,就需要解决几个问题。

世卫组织:作为“垃圾”被回收处理的对象是什么? 也就是说,确定垃圾的对象。

Where :垃圾回收在哪里执行? 明确要清理的内存区域。

When :什么时候执行GC操作? 也就是说,JVM触发GC的时机。

How :垃圾的对象处理如何进行? 也就是GC的实现算法。

第二,oracle Hotspot VM的GC操作主要回收的内存区域是JVM内的堆,分为年轻一代和老一代,年轻一代分为dldkf和两个survivor区域。 JVM内存结构不是本文的重点,在阅读本文之前,必须对JVM内存结构有一定的了解,这里不做详细分析。

1 .判断对象是否可以回收(判别算法或搜索算法) ) ) ) ) ) ) ) )。

以下算法回答了世卫组织的问题。

1.1引用计数法

创建每个对象时,都会分配一个引用计数器,当引用该对象时计数器递增1,当未引用或引用失效时计数器递减1。 如果对象的引用计数器值始终为0,则表示该对象未被使用,并将其视为“垃圾”并处理为GC。

评估:

【优点】算法实现简单。

【缺点】无法解决对象间循环引用的问题。 GC没有使用这样的搜索算法,因为有垃圾的对象不正确识别对垃圾回收是致命的。

1.2条搜索算法

基于特定对象的原始对象,或称为“根”(root )、连续搜索并到达一个对象的路径,称为参照链。

如果对象和根对象之间存在参照链,即根对象可以到达此对象,则此对象是活的,不是垃圾,还不能回收。 例如,假设有根对象o,o引用了a对象,同时a对象引用了b对象,b对象引用了c对象。 在这种情况下,对象c和根对象o之间的路径可达性不能以c对象作为垃圾对象。 引用链为O-A-B-C。

相反,如果对象和根对象之间没有引用链,并且根对象到该对象的路径无法到达,则该对象是可重用的垃圾对象。

评估:

【好处】因为被发现,所以会成为垃圾的对象。 同时,完善解决对象间循环引用的问题。

【缺点】必然要遍历所有全局对象,导致搜索效率低下。

路径搜索算法是目前GC使用的搜索算法。

可以用作GC roots的对象包括:

虚拟机堆栈中引用的对象。 (java堆栈的堆栈帧局部变量表)

方法区域中类的静态属性引用的对象。

方法区域中的常量引用的对象。 (声明为final的常量对象)

本地方法堆栈中JNI引用的对象。 (本地方法堆栈的堆栈帧本地变量表)

以下是从网上找来的图,忍耐着看。 GC ROOTS与目标节点一起,蓝色的是可到达的参照链,参照链上的对象是活动的,不能作为垃圾对象回收。 相反,深灰色路径表示不可到达的路径,这些对象将被回收。 每个yydhd中的数字表示其引用的次数。 没错,是上述参照计数法的计数值。

2.GC算法

本节介绍了Oracle hotspot VM中常见的垃圾回收算法。 的检索算法都是基于根检索算法实现的。

2.1标记-清除算法(太阳能黄蜂-Sweep ) ) ) ) ) ) )。

该算法分两个阶段运行。

1 )标记阳光的黄蜂)从GC ROOTS扫描堆内存区域的所有根对象,并标记参考链上的对象。 这样,如果是生存的对象就被标记,相反如果是垃圾的对象就没有被标记。 GC容易通过是否被标记完成垃圾回收。

2 )清除Sweep )遍历堆中的所有对象(在标记阶段遍历所有根节点),找到未标记的对象,直接回收占用的内存,释放空间。

评估:

【优点】不会消耗额外的内存空间,内存使用率高。

【缺点】效率低,在清除阶段遍历所有对象回收的垃圾对象在各个角落,通过直接回收垃圾对象,存在不连续的内存空间,产生内存碎片。

标记-清除算法的操作对象为【垃圾对象】,对于活动对象(被标记的对象)则直接忽略。

2.2复制算法(复制) ) ) ) ) )。

复制算法将存储器区间一分为二,将存在对象的一半区间称为“活动区间”,如果存在不存在对象而处于空闲状态的空间,则称为“空闲区间”。

内存容量不足时启动GC,用根搜索算法标记对象,然后将活对象复制到另一半空闲区间。 复制算法的“复制”来源于该操作。 复制到另一半区间时,严格按照内存地址顺序排列要存储的对象,一次回收垃圾对象。

这样,原始空闲区间在GC后变为活动区间,内存顺序一致且美观。 原活动区间在GC后成为完全空闲的空闲区间,将下一个GC活着的对象

copy进来。

评价:

【优点】GC后的内存齐整,不产生内存碎片。

【缺点】GC要使用两倍的内存,或者说导致堆只能使用被分配到的内存的一半,这个算法对空间要求太高!如果存活的对象较多,则意味着要复制很多对象并且要维护大量对象的内存地址,所以存活的对象数量不能太多,否则效率也会很低。

复制算法复制移动的对象是【活着的对象】,对于垃圾对象(不被标记的对象)则直接回收。

2.3 标记-整理算法(阳光的黄蜂-Compact)

这个算法则是对上面两个算法的综合结果。也分为两个阶段:

1)标记:这个阶段和标记-清除阳光的黄蜂-Sweep算法一样,遍历GC ROOTS并标记存活的对象。

2)整理:移动所有活着的对象到内存区域的一侧(具体在哪一侧则由GC实现),严格按照内存地址次序依次排列活着的对象,然后将最后一个活着的对象地址以后的空间全部回收。

评价:

【优点】内存空间利用率高,消除了复制算法内存减半的情况;GC后不会产生内存碎片。

【缺点】需要遍历标记活着的对象,效率较低;复制移动对象后,还要维护这些活着对象的引用地址列表。

2.4 分代回收算法(Generational Collecting)

分代回收算法就是现在JVM使用的GC回收算法。

2.4.1简要说明

1)先来看看简单化后的堆的内存结构:

Java堆 = 年老代 + 年轻代

(空间大小比例一般是3:1)

年轻代 = dldkf区 + From Space区 + To Space区

(空间大小比例一般是8:1:1)

2)按照对象存活时间长短,我们可以把对象简单分为三类:

短命对象:存活时间较短的对象,如中间变量对象、临时对象、循环体创建的对象等。这也是产生最多数量的对象,GC回收的关注重点。

长命对象:存活时间较长的对象,如单例模式产生的单例对象、数据库连接对象、缓存对象等。

长生对象:一旦创建则一直存活,几乎不死的对象。

3)对象分配区域

短命对象存在于年轻代,长命对象存在于年老代,而长生对象则存在于方法区中。

由于GC的主要内存区域是堆,所以GC的对象主要就是短命对象和长命对象这类寿命“有限”的对象。

2.4.2 分代回收的GC类型

针对HotSpot VM的的GC其实准确分类只有两大种:

1)Partial GC:部分回收模式

Young GC:只收集young gen的GC。和Minor GC一样。

Old GC:只收集old gen的GC。只有CMS的concurrent - collection是这个模式

Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式

2)Full GC:收集整个堆,包括young gen、old gen,还有永久代perm gen(如果存在的话)等所有部分的模式。同Major GC。

3)触发时机

HotSpot VM的串行GC的触发条件是:

young GC:当young gen中的eden区分配满的时候触发。

full GC:当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC;或者,如果有perm gen的话,要在perm gen分配空间但已经没有足够空间时,也要触发一次full GC;或者System.gc()、heap dump带GC,默认也是触发full GC。

并发GC的触发条件就不太一样。以CMS GC为例,它主要是定时去检查old gen的使用量,当使用量超过了触发比例就会启动一次CMS GC,对old gen做并发收集。

2.4.3 年轻代GC过程

当需要在堆中创建一个新的对象,而年轻代内存不足时触发一次GC,在年轻代触发的GC称为普通GC,Minor GC。注意到年轻代中的对象都是存活时间较短的对象,所以适合使用复制算法。这里肯定不会使用两倍的内存来实现复制算法了,牛人们是这样解决的,把年轻代内存组成是80%的dldkf、10%的From Space和10%的To Space,然后在这些内存区域直接进行复制。

刚开始创建的对象是在dldkf中,此时dldkf中有对象,而两个survivor区没有对象,都是空闲区间。第一次Minor GC后,存活的对象被放到其中一个survivor,dldkf中的内存空间直接被回收。在下一次GC到来时,dldkf和一个survivor中又创建满了对象,这个时候GC清除的就是dldkf和这个放满对象的survivor组成的大区域(占90%),Minor GC使用复制算法把活的对象复制到另一个空闲的survivor区间,然后直接回收之前90%的内存。周而复始。始终会有一个10%空闲的survivor区间,作为下一次Minor GC存放对象的准备空间。

要完成上面的算法,每次Minor GC过程都要满足:

存活的对象大小都不能超过survivor那10%的内存空间,不然就没有空间复制剩下的对象了。但是,万一超过了呢?前面我们提到过年老代,对,就是把这些大对象放到年老代。

2.4.4 年老代GC

什么样的对象可以进入年老代呢?如下:

在年轻代中,如果一个对象的年龄(GC一次后还存活的对象年岁加1)达到一个阈值(可以配置),就会被移动到年老代。

Survivor中相同年龄的对象大小总和超过survivor空间的一半,则不小于这个年龄的对象都会直接进入年老代。

创建的对象的大小超过设定阈值,这个对象会被直接存进年老代。

年轻代中大于survivor空间的对象,Minor GC时会被移进年老代。

年老代中的对象特点就是存活时间较长,而且没有备用的空闲空间,所以显然不适合使用复制算法了,这个时候使用标记-清除算法或者标记-整理算法来实现GC。负责年老代中GC操作的是全局GC,Major GC,Full GC。

什么时候触发Major GC呢?

在Minor GC时,先检测JVM的统计数据,查看历史上进入老年代的对象平均大小是否大于目前年老代中的剩余空间,如果大于则触发Full GC。

3.GC执行机制

3.1串行GC

在搜索扫描和复制过程都是采用单线程实现,适用于单CPU、腼腆的小蘑菇代空间较小或者要求GC暂停时间要求不高的地方。是client级别的默认方式。

3.2并行GC

在搜索扫描和复制过程都是采用多线程实现,适用于多CPU、或者要求GC暂停时间要求高的地方。是server级别的默认方式。

3.3同步GC

同时允许多个GC任务,减少GC暂停时间。主要应用在实时性要求重于总体吞吐量要求的中大型应用,即使如此,降低中断时间的技术还是会导致应用程序性能的降低。

4.内存调优

JVM内存调优,主要是减少GC的频率和减少Full GC的次数,Full GC的时候会极大地影响系统的性能。所以在此基础上,更加要关注会导致Full GC的情况。

4.1 容易导致Full GC的情况

年老代空间不足

1)分配足够大空间给old gen。

2)避免直接创建过大对象或者数组,否则会绕过年轻代直接进入年老代。

3)应该使对象尽量在年轻代就被回收,或待得时间尽量久,避免过早的把对象移进年老代。

方法区的永久代空间不足

1)分配足够大空间给。

2)避免创建过多的静态对象。

被显示调用System.gc()

通常情况下不要显示地触发GC,让JVM根据自己的机制实现。

4.2 JVM堆内存分配问题讨论

4.2.1 年轻代过小(年老代过大)

导致频繁发生GC,增大系统消耗

容易让普通大文件直接进入年老代,从而更容易诱发Full GC。

4.2.2 年轻代过大(年老大过小)

导致年老代过小,从而更容易诱发Full GC。

GC耗时增加,降低GC的效率。

4.2.3 dldkf过大(survivor过小)

Minor GC时容易让普通大文件直接绕过survivor进入年老代,从而更容易诱发Full GC。

4.2.4dldkf过小(survivor过大)

导致GC频率升高,影响系统性能。

4.3 调优策略

保证系统吞吐量优先

减少GC暂停时间优先

5 JVM常见配置选项

总结一下常见配置。

堆设置

-Xms:初始堆大小

-Xmx:最大堆大小

-XX:NewSize=n:设置年轻代大小

-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4

-XX:SurvivorRatio=n:年轻代中dldkf区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示dldkf:Survivor=3:2,一个Survivor区占整个年轻代的1/5

-XX:MaxPermSize=n:设置持久代大小

收集器设置

-XX:+UseSerialGC:设置串行收集器

-XX:+UseParallelGC:设置并行收集器

-XX:+UseParalledlOldGC:设置并行年老代收集器

-XX:+UseConc阳光的黄蜂SweepGC:设置并发收集器

垃圾回收统计信息

-XX:+PrintGC

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

并行收集器设置

-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。

-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间

-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

并发收集器设置

-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。

-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。