Java GC的基础知识

最近碰到一些应用问题,涉及到了Java中的垃圾回收机制,Garbage Collection,简称GC,这其中的学问,还是不少的,有很多东西需要学习。

首先,关于GC的定义,简单讲,他就是Java垃圾回收机制。目前主流的JVM(HotSpot)采用的是分代收集算法。与C++不同的是,Java采用的是类似于树形结构的可达性分析法来判断对象是否还存在引用。即从gcroot开始,把所有可以搜索得到的对象标记为存活对象。

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

堆(Heap),是JVM管理的内存中最大的一块。程序的主要数据也都是存放在堆内存中的,这一块区域被所有的线程所共享,通常出现线程安全问题的一般都是这个区域的数据出现的问题。

方法区(Method Area),与Heap一样,也是各个线程共享的内存域,这块区域主要是用来存储类加载器加载的类信息,常量,静态变量,通俗的讲就是编译后的class文件信息。

JVM栈,与程序计数器一样,它是每个线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

本地方法栈(Native Method Stacks),他和虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

程序计数器,是为多线程准备的,程序计数器是每个线程独有的,所以是线程安全的。它主要用于记录每个线程的执行情况。

通常我们所说的gc主要是针对java heap这块区域的。下面来了解一下heap区。

从图中我们可以看出JVM heap区域是分代的,分为年轻代,老年代和持久代。JVM的堆区对象分配的一般规则:

(1)对象优先在Eden区分配。

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

(3)长期存活的对象将进入老年代。

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

(5)空间分配担保,只要老年代的连续空间大于(新生代所有对象的总大小或者历次晋升的平均大小)就会进行minor GC,否则会进行full GC。

现在我们看下,什么情况下,会触发GC,主要有这几种,

(1)System.gc()方法的调用。此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下他会触发Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+DisableExplicitGC来禁止RMI(Java远程方法调用)调用System.gc。

(2)旧生代空间不足。旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出错误:java.lang.OutOfMemoryError: Java heap space 。为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。从数值上看,老年代的使用率达到阈值(通过JVM参数:CMSInitiatingOccupancyFraction设定,默认为92%)。

(3)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。从数值上看,永久代的使用率到达阈值CMSInitiatingPermOccupancyFraction设定,默认为92%)。

(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存。如果发现统计数据说之前Minor GC的平均晋升大小比目前old gen剩余的空间大,则不会触发Minor GC而是转为触发full GC。

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

GC是垃圾回收,回收的是什么?通过以上信息,其实可以看到GC的主要作用是回收堆中的对象。通过可达性分析一个对象的引用是否存在,如果不存在,就可以被回收了。

GC是如何实现回收对象的?主要看是用的哪一种回收算法以及用的什么垃圾回收器了。

回收算法主要有标记-清除复制算法、标记-整理(Mark-Compat)算法、分代收集(Generational Collection)算法。这里针对不同的代,可以使用一些相对合适的算法。

新生代中,每次垃圾收集时都有大批对象死去,只有少量存活,就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

老年代中,其存活率较高、没有额外空间对它进行分配担保,就应该使用“标记-整理”或“标记-清理”算法进行回收。

常用的垃圾回收器:

(1) Serial收集器。

(2) ParNew收集器。

(3) Parallel Scavenge收集器

(4) CMS(Concurrent Mark Sweep)收集器

(5) G1(Garbage First)收集器(从JDK1.7 Update 14之后的HotSpot虚拟机正式提供了商用的G1收集器)。

几种垃圾收集器比对,

关于垃圾回收器的使用,这里有个组合建议供大家参考:

新生代GC方式

旧生代和持久代GC方式

-XX:+UseSerialGC

串行GC

串行GC

-XX:+UseParallelGC

PS GC

并行MSC GC

-XX:+UseConcMarkSweepGC

ParNew GC

并发GC当出现concurrent Mode failure时采用串行GC

-XX:+UseParNewGC

并行GC

串行GC

-XX:+UseParallelOldGC

PS GC

并行Compacting GC

-XX:+UseConcMarkSweepGC-XX:-UseParNewGC

串行GC

并发GC当出现concurrent Mode failure或promotion failed时则采用串行GC

不支持的组合方式

1、-XX:+UseParNewGC -XX:+UseParallelOldGC2、-XX:+UseParNewGC -XX:+UseSerialGC

参考:https://www.cnblogs.com/zwh-Seeking/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 小白是怎么搞懂GC全过程?

    冯大师在架构师进阶之路写了一篇文章,通俗易懂地介绍了什么是GC,受益匪浅,学习一下。

    bisal
  • Oracle 12c CC安装部署攻略 (下)

    11g GC下可以选择agentdownload方式从客户端安装,但之前有时能做有时不行,从server端安装一样,时行时不行,不稳定,还要总来调试。12c C...

    bisal
  • Cloud Compute

           最近,Cloud Compute,也就是“云计算”备受关注,或者说是因为一些大厂商铺天盖地的宣传才导致自web2.0以来又一个互联网应用的new ...

    bisal
  • 每日一个知识点:什么时候会触发Full GC

    只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。

    架构技术专栏
  • java中的垃圾回收机制简介

    内存空间是有限的,运行时如果不能获取到内存,会抛出OutOfMemory,一种有效的解决措施是,抛弃那些程序永远不会不再用到的对象,腾出空间。

    爬蜥
  • JVM笔记九-GC收集器日志信息学习

    在上一篇文章中,我们通过代码运行结果,查看到JVM的堆内存逻辑上分区是三部分,物理上分区是2部分,以及是新生代分区三部分,占比分布是8/1/1。而且我们还通过代...

    凯哥Java
  • 什么时候触发GC

    所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给...

    王小明_HIT
  • Java底层-GC子系统

    在HotSpot虚拟机中,三大子系统核心基本都是为运行时数据区服务,类加载子系统负责将字节码文件加载到运行时数据区, 执行引擎将执行线程中虚拟机栈的栈帧存储的指...

    每天学Java
  • 面试杂谈 - 谈谈你对GC的理解

    接下来就对GC做一个全方位的总结,希望下次可以自信地回答面试官:我是可以被贵公司回收的那种。

    acupt
  • 笔者带你剖析大规模分布式Java平台JVM性能调优基础

    其实说到对JVM进行性能调优早已是一个老生常谈的话题,如果你所在的技术团队还暂时达不到淘宝团队那样的高度,无法满足在OpenJDK的基础之上根据自身业务进行针对...

    九州暮云

扫码关注云+社区

领取腾讯云代金券