首页 > 编程知识 正文

java的gc模式,什么是java中的fullgc

时间:2023-05-06 09:41:12 阅读:228001 作者:1821

本文共3198字,是本人前几天面试被提问到的一个问题,将在该文中阐述关于Java垃圾回收——Full GC的相关知识,包括定义、触发条件、具体过程。

前几天面试的时候,面试官在最后问了我一个有关Full GC的垃圾回收问题,希望我说明下该GC的定义、以及是如何触发的问题,有感便记下这篇文章。

垃圾回收的定义

首先我们要知道GC这个概念,即就是我们常说的Java垃圾回收机制,Java通过可达性分析一个对象的引用是否存在,当不存在的时候,将回收堆中的对象。

目前主流的JVM,也就是HotSpot,采用的是分代收集算法。分代的含义涉及到Java的运行时数据区域,在下面几个环节我们来简单描述下Java的内存结构、FullGC的触发以及分代收集算法是对应的哪些内存区域。

垃圾回收相关基础知识

一般而言,GC主要是针对运行的数据区的。作为程序员要关注的区域主要有5块,分别是方法区(Method Area),Java栈(Java stack),本地方法栈(Native Method Stack),堆(Heap),程序计数器(Program Counter Register)。实际jvm在管理内存的时候,比这个分的更细致,只不过做应用程序开发,我们只需要关注这5块就可以了。

可以关注到,其中有一个区域叫做“堆”,也就是Heap了,它是Jvm管理的内存中最大的一块。程序的主要数据也都是存放在堆内存中的,也就是说程序所创建的对象基本上都在该区域进行内存分配,这一块区域被所有的线程所共享,通常出现线程安全问题的一般都是这个区域的数据出现的问题。通常我们所说的gc主要是针对java heap这块区域的。下面来了解一下heap区。

可见,Heap区在设计上是分代设计的,其划分为了tmddm、Survivor 和 Tenured/Old ,其中tmddm区、Survivor(存活)属于年轻代,Tenured/Old区属于老年代或者持久代。

一般我们将年轻代发生的GC称为Minor GC,对老年代进行GC称为Major GC。

而本文所要阐述的FullGC是对整个堆来说的,在最近几个版本的JDK里默认包括了对永生带即方法区的回收(JDK8中无永生带了),出现Full GC的时候经常伴随至少一次的Minor GC,但非绝对的。Major GC的速度一般会比Minor GC慢10倍以上。

到这里,可能会有些人对heap分区的意义不太理解,我们在这里就简单介绍下年轻代和老年代是如何进行工作的,我们需要知道关于JVM的堆区对象分配的一般规则:

1. 对象优先在tmddm区分配

2. 大对象直接进入老年代(-XX:PretenureSizeThreshold=3145728 这个参数来定义多大的对象直接进入老年代)

3. 长期存活的对象将进入老年代(在JDK8中测试,-XX:MaxTenuringThreshold=1的阀值设定根本没用)

4. 动态对象年龄判定(虚拟机并不会永远地要求对象的年龄都必须达到MaxTenuringThreshold才能晋升老年代,如果Survivor空间中相同年龄的所有对象的大小总和大于Survivor的一半,年龄大于或等于该年龄的对象就可以直接进入老年代)

5. 空间分配担保

6. 只要老年代的连续空间大于(花痴的太阳代所有对象的总大小或者历次晋升的平均大小)就会进行minor GC,否则会进行full GC。

FullGC的触发条件

在了解了关于GC的一些相关知识后,我们需要知道FullGC的触发条件(这个是主要提问到点)。

上个环节我们知道了,FullGC是针对整个Heap区而言的,它将在以下几种情况被触发:

在程序中调用了System.gc()方法。此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI(Java远程方法调用)调用System.gc。老年代空间不足。老年代空间只有在花痴的太阳代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误【java.lang.OutOfMemoryError: Java heap space】,而为避免以上两种状况引起的Full GC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在花痴的太阳代多存活一段时间及不要创建过大的对象及数组。Permanet Generation空间满了。也就是以前所说的方法区,Permanet Generation中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出错误信息:java.lang.OutOfMemoryError: PermGen space 。为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。通过Minor GC后进入老年代的平均大小大于老年代的可用内存。如果发现统计数据说之前Minor GC的平均晋升大小比目前old gen剩余的空间大,则不会触发Minor GC而是转为触发full GC。由tmddm区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。分配很大的对象。所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC。为了解决这个问题,CMS垃圾收集器提供了一个可配置的参数,即-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费赠送一个碎片整理的过程,内存整理的过程无法并发的,空间碎片问题没有了,但停顿时间不得不变长了,JVM设计者们还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。常见的垃圾回收器——回收过程

垃圾回收器并不是本文的主题,但还是想简单列举一下:

Serial收集器ParNew收集器Parallel Scavenge收集器CMS(Concurrent Mark Sweep)收集器G1(Garbage First)收集器(从JDK1.7 Update 14之后的HotSpot虚拟机正式提供了商用的G1收集器)

不知道各位还好不好奇上上个环节说到的分代收集算法。

大致我们也能猜到,其实就是针对年轻代和老年代,JVM将使用不同的垃圾收集算法进行收集,达到高效的垃圾回收。

年轻代采用的是标记-复制算法,将需要回收的对象标记,将不需要的对象移动到Survivor空间,然后将标记对象回收,该算法可以实现对大多数会失效的对象进行回收,对少部分不需要回收的对象进行转移,保证eden区拥有连续的内存空间,而且复制的效率高。

因为在年轻代不需要回收的对象一般是很少的,每次垃圾收集时都有大批对象死去,只有少量存活,选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

老年代采用的是标记-整理算法,将需要回收的对象标记,将不需要的对象进行移动整理,使不需要回收的对象占用连续的内存空间,再清除回收对象,保证老年代拥有连续的内存空间,而且整理效率高。

因为在老年代需要回收的对象一般是很少的,其存活率较高、没有额外空间对它进行分配担保。

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