JVM内存管理之GC垃圾回收体系

从四则运算引发对JVM的认识之后,对象进行垃圾收集?如何去进行收集?

>>>https://blog.csdn.net/yxd179/article/details/84189340?yxd179

First、确定对象是否需要回收:引用计数法可达性算法

引用计数法简述:给对象添加一个引用计数器,若引用则计数器加一,引用失效则减一,计数器为零的对象就是不再被使用的。(其中两个或多个对象之间可能会互相引用对方,导致这样的对象无法被回收。)

可达性算法简述:在一系列的称为"GC Roots"的对象作为start node,从这些node开始向下搜索,搜索所走过的路径称为Reference Chain引用链,当一个对象到GC Roots没有任何引用链相连时,则说明此对象是不可用的。(其中虚拟机栈<栈帧中的本地变量表>中引用对象、方法区中的类静态属性引用的对象、方法区中常量引用的对象<final常量值>、本地方法栈JNI<Native方法>引用的对象可视为GC root的对象,解决了引用计数法的问题。)

Second、GC主要是针对堆的操作:标记清除复制算法标记整理<老年代-主要是把存活对象移到内存的一端>、分代收集<对新生代采用复制算法,对老年代采用标记整理或者标记清除算法>。

垃圾收集器简述>>>

1、串行收集器(Serial Collector,复制算法)>>>新生代单线程收集器(标记和清除都是单线程),是client模式默认的GC,简单高效,也可通过-XX:+UseSerialGC强制指定别的GC方式;

2、串行老年代收集器(Serial Old Collector,清除-标记算法)>>>老年代单线程收集器;

并行新生代收集器(ParNew Collector,停止-复制算法)>>>新生代并行收集器,在多核CPU环境下,比串行收集器效率更高;

3、Parallel Scavenge收集器(停止-复制算法)>>>并行收集器,高效利用CPU以获得高吞吐量(用户线程时间/<用户线程时间+GC线程时间>),server模式默认采用的GC方式;可用-XX:+UseParallelGC和-XX:ParallelGCThreads指定GC方式和GC线程数;

4、Parallel Old Scavenge收集器(停止-复制算法)>>>老年代并行收集器(吞吐量优先);

5、CMS收集器(Concurrent Mark Sweep,标记清理算法)>>>高并发,低停顿(GC回收时暂停应用),响应快,尤适合多CPU。

在图示Eden区域:栈8份,两个survivor区域各占1份,即8:1:1(新生代中98%的对象"朝生夕灭",很少会存活下来,因此就设定了10%的空间来存放活下来的,详细日志-XX:+PrintGCDetails比例-XX:SurvivorRatio=8Xms20M堆内存最小值-Xmx20M堆内存最大值、-Xmn10M堆内存分配给新生代、-XX:PermSize设置持久代堆空间的初始值和最小值、-XX:MaxPermSize=<n>[g|m|k]设置持久代堆空间的最大值)。大多数的新生代都是采用的复制清除法作为垃圾回收算法。当对新生代进行minor gc(发生在新生代的垃圾收集动作,java对象大多都具备"朝生夕灭"的特性,所以Minor GC非常频繁,一般回收速度也比较快)时,会把Eden中和Survivor中的存活对象复制到另一块survivor区域中。)

标记-清除:接标记清除即可,不足的是效率不高,空间会产生大量碎片。

复制:将空间分成两块,每次只对其中一块进行 GC。若这块内存使用完时,将还存活的对象复制到另一块上面去。不足的是会造成空间利用率低下。因为大多数新生代对象都不会熬过第一次 GC,没有必要分配1:1的划分空间,如图,可分一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 空间和其中一块 Survivor。当回收的时候,将 Eden 和 Survivor 中还存活的对象一次性复制到另一块 Survivor 上,最后去清理 Eden 和 Survivor 空间。比例一般是 8 : 1 : 1,但每次浪费 10% 的 Survivor 空间。

新生代每次垃圾回收都有大量对象死去,只有少量存活,选用复制算法比较合理,而老年代中对象存活率较高、没有额外的空间分配对它进行担保,使用标记清除或标记整理算法回收。

若是出现了不止10%的对象存活下来呢?多出来的对象直接进入老年代

则从新生代晋升到老年代:对象熬过一次GC,年龄加一(MaxTenuringThreshold),当达到设定的阈值,则可进入老年代;比较大的对象(需要大量连续的内存空间PretenureSizeThreshold,默认15),当对象大于这个值,则可直接进入老年代;Survivor空间中相同年龄的对象大小总和大于Survivor空间的一半(即上述情况),则年龄大于或等于该年龄的对象就可以进入老年代。(动态判定,适应那些内存较小的情况,survivor from和 survivor to之间不停互换角色。)

那GC是否可直接内存嚒?why not?(Direct Memory:系统物理内存,不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。在jdk1.4中新加入了NIO类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作,避免了在Java堆和Native堆中来回复制数据。)

HotSpot的算法:在实际项目中,方法区的内容可能会非常多,引用数量庞大,运行中的java程序的引用情况都是在发生变化的,因此在进行可达性分析的时候,对象引用的关系在某个时间段上,必须是保持不变的,这一点是导致进行GC进行时必须停顿所有java执行线程的一个重要原因——"Stop the world".(Hotspot虚拟机中使用一组称为OopMap的数据结构来维护哪些地方存放着对象引用,Hotspot虚拟机可以快速且准确地完成GC Roots枚举,而选择恰当的时机进行GC仍然关键,这些特定的GC位置被称为SafePoint<是否具有让程序长时间执行的特点。比较典型的如方法调用,循环跳转等都是safepoint的选择>、安全区域<指线程进入到某个区域时就可以被JVM的GC直接忽略。典型的应用场景是线程处于Sleep或者Blocked状态的场景>。)

Dump文件分析>>>https://blog.csdn.net/yxd179/article/details/83307567?yxd179

触发full gcSystem.gc(),Runtime.getRuntime().gc(),java.lang.management.MemoryMXBean.gc()、老年代空间不足、Permanet Generation空间满、CMS GC时出现promotion failed和concurrent mode failure、Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间。

老年代空间不足(在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,执行Full GC后空间仍然不足,抛出java.lang.OutOfMemoryError: Java heap space,为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。)

Permanet Generation(存放class信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。若通过Full GC仍回收不了,出现java.lang.OutOfMemoryError: PermGen space,为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。)

CMS GC时出现promotion failed和concurrent mode failure(对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。 promotionfailed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。 可增大survivorspace、旧生代空间或调低触发并发GC的比率,根据jdk的不同版本,若CMS在remark完毕后很久才触发sweeping动作,可设置-XX:CMSMaxAbortablePrecleanTime=5。)

Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间(Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。 例如程序第一次触发MinorGC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,若小于6MB,则执行Full GC。 当新生代采用PSGC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。 除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。可通过在启动时通过- java-Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc()。)

>>>附达人整理的牛客鹅厂、蚂蚁等大厂面经?

https://www.nowcoder.com/discuss/120239?order=4&pos=23&type=0

>>>附达人整理的JVM类加载机制csdn?

https://blog.csdn.net/topdeveloperr/article/details/80816309#关于OSGI

>>>深入理解Java虚拟机JVM高级特性与最佳实践第2版.pdf?

https://uploadfiles.nowcoder.com/learning_material/20180810/3353131_1533908669399_%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Java%E8%99%9A%E6%8B%9F%E6%9C%BAJVM%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%E7%AC%AC2%E7%89%88.pdf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值