欢迎光临北大青鸟佛山华大校区

我的位置: 首页 >>Java GC 的那些事(1)

Java GC 的那些事(1)

2017-06-14

来源:

  Java GC 的那些事(1)

  前言

  与C语言不同,Java内存(堆内存)的分配与回收由JVM垃圾收集器自动完成,这个特性深受大家欢迎,能够帮助 程序员 更好的编写代码,本文以HotSpot虚拟机为例,说一说Java GC的那些事。

  Java堆内存

  在 JVM内存的那些事 一文中,我们已经知道Java堆是被所有线程共享的一块内存区域,所有对象实例和数组都在堆上进行内存分配。为了进行高效的垃圾回收,虚拟机把堆内存划分成新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)3个区域。

  新生代

  新生代由 Eden 与 Survivor Space(S0,S1)构成,大小通过-Xmn参数指定,Eden 与 Survivor Space 的内存大小比例默认为8:1,可以通过-XX:SurvivorRatio 参数指定,比如新生代为10M 时,Eden分配8M,S0和S1各分配1M。

  Eden:希腊语,意思为伊甸园,在圣经中,伊甸园含有乐园的意思,根据《旧约·创世纪》记载,上帝耶和华照自己的形像造了第一个男人亚当,再用亚当的一个肋骨创造了一个女人夏娃,并安置他们住在了伊甸园。

  大多数情况下,对象在Eden中分配,当Eden没有足够空间时,会触发一次Minor GC,虚拟机提供了-XX:+PrintGCDetails参数,告诉虚拟机在发生垃圾回收时打印内存回收日志。

  Survivor:意思为幸存者,是新生代和老年代的缓冲区域。

  当新生代发生GC(Minor GC)时,会将存活的对象移动到S0内存区域,并清空Eden区域,当再次发生Minor GC时,将Eden和S0中存活的对象移动到S1内存区域。

  存活对象会反复在S0和S1之间移动,当对象从Eden移动到Survivor或者在Survivor之间移动时,对象的GC年龄自动累加,当GC年龄超过默认阈值15时,会将该对象移动到老年代,可以通过参数-XX:MaxTenuringThreshold 对GC年龄的阈值进行设置。

  老年代

  老年代的空间大小即-Xmx 与-Xmn 两个参数之差,用于存放经过几次Minor GC之后依旧存活的对象。当老年代的空间不足时,会触发Major GC/Full GC,速度一般比Minor GC慢10倍以上。

  永久代

  在JDK8之前的HotSpot实现中,类的元数据如方法数据、方法信息(字节码,栈和变量大小)、运行时常量池、已确定的符号引用和虚方法表等被保存在永久代中,32位默认永久代的大小为64M,64位默认为85M,可以通过参数-XX:MaxPermSize进行设置,一旦类的元数据超过了永久代大小,就会抛出OOM异常。

  虚拟机团队在JDK8的HotSpot中,把永久代从Java堆中移除了,并把类的元数据直接保存在本地内存区域(堆外内存),称之为元空间。

  这样做有什么好处?

  有经验的同学会发现,对永久代的调优过程非常困难,永久代的大小很难确定,其中涉及到太多因素,如类的总数、常量池大小和方法数量等,而且永久代的数据可能会随着每一次Full GC而发生移动。

  而在JDK8中,类的元数据保存在本地内存中,元空间的最大可分配空间就是系统可用内存空间,可以避免永久代的内存溢出问题,不过需要监控内存的消耗情况,一旦发生内存泄漏,会占用大量的本地内存。

  ps:JDK7之前的HotSpot,字符串常量池的字符串被存储在永久代中,因此可能导致一系列的性能问题和内存溢出错误。在JDK8中,字符串常量池中只保存字符串的引用。

  如何判断对象是否存活

  GC动作发生之前,需要确定堆内存中哪些对象是存活的,一般有两种方法:引用计数法和可达性分析法。

  1、引用计数法

  在对象上添加一个引用计数器,每当有一个对象引用它时,计数器加1,当使用完该对象时,计数器减1,计数器值为0的对象表示不可能再被使用。

  引用计数法实现简单,判定高效,但不能解决对象之间相互引用的问题。

  public class GCtest {    private Object instance = null;    private static final int _10M = 10 * 1 << 20;    // 一个对象占10M,方便在GC日志中看出是否被回收    private byte[] bigSize = new byte[_10M];    public static void main(String[] args) {        GCtest objA = new GCtest();        GCtest objB = new GCtest();        objA.instance = objB;        objB.instance = objA;        objA = null;        objB = null;        System.gc();    }}

  通过添加-XX:+PrintGC参数,运行结果:

  [GC (System.gc()) [PSYoungGen: 26982K->1194K(75776K)] 26982K->1202K(249344K), 0.0010103 secs]

  从 GC日志 中可以看出objA和objB虽然相互引用,但是它们所占的内存还是被垃圾收集器回收了。

  2、可达性分析法

  通过一系列称为 GC Roots 的对象作为起点,从这些节点开始向下搜索,搜索路径称为 引用链,以下对象可作为GC Roots:

  本地变量表中引用的对象

  方法区中静态变量引用的对象

  方法区中常量引用的对象

  Native方法引用的对象

  当一个对象到 GC Roots 没有任何引用链时,意味着该对象可以被回收。

  在可达性分析法中,判定一个对象objA是否可回收,至少要经历两次标记过程:

  1、如果对象objA到 GC Roots没有引用链,则进行第一次标记。

  2、如果对象objA重写了finalize()方法,且还未执行过,那么objA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize()方法。finalize()方法是对象逃脱死亡的最后机会,GC会对队列中的对象进行第二次标记,如果objA在finalize()方法中与引用链上的任何一个对象建立联系,那么在第二次标记时,objA会被移出即将回收集合。

  看看具体实现

  public class FinalizerTest {    public static FinalizerTest object;    public void isAlive() {        System.out.println(I'm alive);    }    @Override    protected void finalize() throws Throwable {        super.finalize();        System.out.println(method finalize is running);        object = this;    }    public static void main(String[] args) t

开班信息

开班课程 人数 了解详情
名企定向委培班 剩余名额3 课程介绍
ACCP8.0软件培训班 剩余名额8 课程介绍
短期冲刺班 剩余名额2 课程介绍
项目精英班 剩余名额1 课程介绍
NET课程重点班 剩余名额6 课程介绍
软件开发精英班 剩余名额2 课程介绍
JAVA特招班 剩余名额10 课程介绍

免费试听

试听课程 了解详情
名企定向委培班 试听详情
ACCP8.0软件培训班 试听详情
短期冲刺班 试听详情
项目精英班 试听详情
NET课程重点班 试听详情
软件开发精英班 试听详情
JAVA特招班 试听详情

姓名:

电话:

最新就业

姓名 就业岗位 就业企业
占乐乐 软件工程师 关务通网络科技公司
张浩峰 软件工程师 嘉域网络科技有限公司
卜锡龙 软件工程师 必应科技有限公司
梁嘉嘉 软件工程师 林氏木业
何伟煊 java工程师 金政信息科技
张雅涵 NET工程师 国贸集团股份有限公司
张默 项目组长 极宇舟天有限公司
张婧涵 NET工程师 大族激光有限公司
张洪文 NET工程师 诚迈科技有限公司
王玥婷 java技术顾问岗位 百纳威尔有限公司
王鸿轩 Java开发岗位 爱卡智能
邓安志 软件开发工程师 美的集团
何浚宏 软件开发课程工程师 东和科技有限公司
黄隆 软件开发顾问 建发股份公司
刘应琴 软件开发工程师 国美集团
刘雨林 java工程师 唯品会科技有限公司
彭子昂 Java开发 爱卡智能有限公司
  • 佛山华大校区
  • 厦门软件园校区
  • 福州新生代校区
  • 厦门课工场
  • 北大青鸟佛山华大校区
  • 地址:广东省佛山市禅城区祖庙路1号富荣大厦4楼
  • 乘车路线:旋宫酒店站、亲仁路站、松风路站、莲花站
  • 招生电话:0757-88726000    4006-989-522
  • 网址:www.foshanbdqn.com
  • 北大青鸟厦门软件园校区
  • 地址:厦门市思明区软件园二期观日路48号(IT实训32号)
  • 乘车路线:软件园站、软件园南门、软件园东二门站、观日路口
  • 招生电话:0592-5920811    4000-470-150
  • 网址:www.xmbdqn.com
  • 北大青鸟福州新生代校区
  • 地址:福州市鼓楼区东大路恒裕大厦三楼
  • 乘车路线:塔头站、东水路口站、八方大厦站
  • 招生电话:0591-87880522    400-9966-370
  • 网址:www.0591bdqn.com
  • 北大青鸟厦门课工场
  • 地址:厦门市思明区软件园二期观日路48号(IT实训32号)
  • 乘车路线:软件园站、软件园南门、软件园东二门站、观日路口
  • 招生电话:0592-5920811    4000-470-150
  • 网址:www.xmbdqn.com

佛山华大校区微信公众平台

佛山北大青鸟