`
足至迹留
  • 浏览: 486086 次
  • 性别: Icon_minigender_1
  • 来自: OnePiece
社区版块
存档分类
最新评论

<2-2> 垃圾收集器与内存分配

阅读更多
2.4 垃圾收集器
如果说垃圾收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商,不同版本的虚拟机所提供的垃圾收集器都可能会有很大的差别,并且一般都会提供参数共用户根据自己的应用特点和要求组合出各个年代所使用的收集器。这里讨论的收集器基于Sun HotSpot虚拟机1.6版Update 22,这个虚拟机包含的所有收集器如图:


上图展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明他们可以搭配使用。具体收集器的实现和特点这里就不分析了,需要的话可以查看详细资料。

2.5 内存分配与回收策略  ---回答何时回收
上一篇《2-1》里解答了java内存的两个问题,回收哪些内存,如何回收内存。这一小节重点讲述内存分配,但也间接回答了何时回收内存。
Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存。关于回收内存这一点,我们已经介绍了挺多,现在我们再看下给对象分配内存的那点事。

对象的内存分配,往大方向说,就是堆上分配(但也可能经过JIT编译后被拆散为标量类型并间接地在栈上分配),对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况下也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存有关的参数设置。
接下来我们将会介绍几条最普遍的内存分配规则。

2.5.1 对象优先在Eden分配
在大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机就发起一次Minor GC。
虚拟机提供了-XX:+PrintGCDetails这个收集器日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出时输出当前内存各区域的分配情况。在实际应用中,内存回收日志一般是打印到文件后通过日志工具进行分析。
下面从实例验证内存分配、Minor GC的时机以及学习查看GC日志。这里先介绍下Minor GC和Full GC:
(1) Minor GC,就是新生代GC。指发生在新生代的垃圾收集动作,因为java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。
(2) Full GC, 也叫Major GC, 老年代GC。指发生在老年代的GC,出现了Major GC, 经常伴随至少一次Minor GC(但也不是绝对的,在ParallelScavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。


上面例子尝试分配3个2MB的对象和1个4MB的对象。在运行时通过-Xmx20M, -Xms20M和-Xmn10M这3个参数限制java堆大小为20M,且不可扩展,其中10M分配给新生代,剩下的10M分配给老年代。默认的-XX:SurvivorRatio=8决定了新生代中Eden区与一个Survivor区的空间比例是8比1,新生代总可用空间为9M。

理论分析上面的代码,执行testAllocation()中分配allocation4对象的语句时会发生一次MinorGC,因为分配4M内存时发现Eden已经被占用了6M, 可用的只有3M,因此发生了Minor GC。GC期间又发现已有的3个2MB的对象全部无法放入1MB的Survivor空间,所以只好通过分配担保机制提前转移到老年代中。
这次GC结束后,4MB的allocation4对象被顺利分配在Eden中。因此程序执行完的结果是Eden占用4MB, Survivor空闲,老年代被占用6MB。
现在查看运行结果输出的GC日志:


从GC日志我们看出,垃圾回收后新生代内存明显减少,但总堆区内存变化不大,主要是6M内存从新生代转移到了老年代。
关于GC日志的查看,可以参考:
http://blog.csdn.net/huangzhaoyang2009/article/details/11860757
http://wenku.baidu.com/link?url=HNGi_DOsSPWnxFyXFXpyB1gkNpndcgOom6kwU22KSnuTasH8hxZvzf_xfY4Au21vOc60HMlXWXyFYCXyDjJykPM4QpPK-6Ob6bikfGIN4Y_
简单的日志可以人工阅读,复杂的日志还是要借助专门的gC日志分析工具来分析。

2.5.2 大对象直接进入老年代
所谓大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串及数组。大对象对虚拟机的内存分配来说就是一个坏消息(比遇到一个大对象更加坏的消息就是遇到一群“朝生夕灭”的“短命大对象”,写程序的时候应当避免),经常出现大对象容易导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间来安置它们。
虚拟机提供了一个-XX:PretenureSizeThreshold参数(只是对Serial和ParNew两款收集器有效),令大于这个设置值得对象直接到老年代中分配。这样做的目的是避免在Eden区以及两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。

2.5.3 长期存活的对象将进入老年代
虚拟机既然采用了分代收集的思想来管理内存,那内存回收时就必须能识别哪些对象应当放在新生代,哪些对象应放在老年代中。为了做到这点,虚拟机给这个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1.对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认15岁)时,就会晋升到老年代中。对象晋升老年代的年龄阀值,可以通过-XX:MaxTenuringThreshold设置。

2.5.4 动态对象年龄判定
为了能更好地适应不同程序的内存状况,虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到maxTenuringThreshlold中要求的年龄。

2.5.5 空间分配担保
在发生Minor GC时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代剩余空间大小,如果大于,则改为直接进行一次Full GC。如果小于,则查看HandlePromotionFailure设置是否允许担保失败;如果允许,那只会进行Minor GC;如果不允许,那也要改为进行一次Full GC。
前面提到过,新生代使用复制收集算法,但为了内存利用率,只使用其中一个Survivor空间来作为轮换备份,因此当出现大量对象在Minor GC后仍然存活的情况时(最极端就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,让Survivor无法容纳的对象直接进入老年代。与生活中的贷款担保类似,老年代要进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会活下来,在实际完成内存回收之前是无法明确知道的,所以只好取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行full GC来让老年代腾出更多空间。

取平均值进行比较其实仍然是一种动态概率的手段,也就是说如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然会导致担保失败,那就只好在失败后重新出发一次Full GC。虽然担保失败时绕的圈子是最大的,但大部分情况下都还是会将HandlePromotionFailure开关打开,避免full GC过于频繁。

2.6 小结
《2-1》和本文《2-2》介绍了垃圾收集的算法思想,以及java虚拟机中自动该内存分配及回收的主要规则。
内存回收与垃圾收集器在很多时候都是影响系统性能,并发能力的主要因素之一,虚拟机之所以提供多种不同的收集器及大量的调节参数,是因为只有根据实际应用需求,实现方式选择最优的收集方式才能获取最好的性能。没有固定收集器,参数组合,也没有最优的调优方法,虚拟机也没有什么必然的内存回收行为。因此学习虚拟机内存知识,如果要用到实践调优阶段,必须了解每个具体收集器的行为,优势和劣势、调节参数。

参考资料:
《深入理解java虚拟机》
  • 大小: 158.9 KB
  • 大小: 82.8 KB
  • 大小: 99.4 KB
0
0
分享到:
评论

相关推荐

    【Java正来-Java虚拟机专题】-Java垃圾收集器与内存分配策略

    主要整理内容为:分析了垃圾收集的算法和JDK1.7中提供的7款垃圾收集器的特点以及运作原理。以及内存分配策略

    JVM初探- 内存分配、GC原理与垃圾收集器

    JVM初探- 内存分配、GC原理与垃圾收集器,从从提上讲解了jvm中GC的原理、基本的算法和针对不同内存区使用的算法,同时,详细的讲解了当前主要使用的垃圾收集器

    JAVA 垃圾收集器与内存分配策略.rar

    JAVA 垃圾收集器与内存分配策略.rar

    Eclipse 启动运行速度调优

    &lt;br&gt;运行参数如下:&lt;br&gt;eclipse.exe -vmargs -Xverify:none -XX:+UseParallelGC -XX:PermSize=20M &lt;br&gt;&lt;br&gt;-------------- &lt;br&gt;&lt;br&gt;JVM 提供了各种用于调整内存分配和垃圾回收行为的标准开关和非标准...

    JVM初探内存分配GC原理与垃圾收集器共16页.pdf.z

    JVM初探内存分配GC原理与垃圾收集器共16页.pdf.zip

    JVM内存分配与垃圾回收详解

    个人整理 jvm相关知识 包括内存分配机制 垃圾回收机制 垃圾收集器相关 及 垃圾收集算法

    JAVA垃圾收集器与内存分配策略详解

    介绍了JAVA垃圾收集器与内存分配策略,需要了解的朋友可以参考下

    垃圾回收器和内存分配.rar

    G1收集器的特点计算法、 未来的垃圾回收ZGC, Stop the World现象及内存分配策略

    《垃圾收集》(Garbage Collection)扫描版[PDF]——part2

    10.2 对C++垃圾收集器的需求 10.3 在编译器中还是在库中 10.4 保守式垃圾收集 10.5 准复制式收集器 10.6 智能指针 10.6.1 在没有智能指针类层次的情况下进行转换 10.6.2 多重继承 10.6.3 不正确的转换 10.6.4 某些...

    内存管理内存管理内存管理

    文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半手工地管理内存,以及如何使用垃圾收集自动管理内存。 为什么必须管理内存 内存管理是计算机编程最为基本的...

    elementary_gc:2021年Spring用于CS296-41的垃圾收集器

    SP21 CS296-41项目的垃圾收集器 待办事项清单 (自下而上排序) 内存压缩 实现空闲对象的内存压缩 世代GC 增加对象元数据的年龄 为每个年龄段创建单独的列表 每个组以不同的时间间隔运行垃圾收集器 使用GC ...

    操作系统(内存管理)

    文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半手工地管理内存,以及如何使用垃圾收集自动管理内存。 为什么必须管理内存 内存管理是计算机编程最为基本的...

    【JVM和性能优化】2.垃圾回收器和内存分配策略

    Compact)GC算法综合用年轻代老年代永久代枚举根节点安全点安全区域GC回收器Serial 收集器ParNew 收集器Parallel Scavenge 收集器Serial Old 收集器Parallel Old 收集器CMS 收集器G1 收集器ZGCSTW实现内存分配与回收...

    mapdb:MapDB提供由磁盘存储或堆外内存支持的并发Maps,Sets和Queues。 它是一种易于使用的嵌入式Java数据库引擎

    不受垃圾收集器影响的堆外收集 具有到期和磁盘溢出的多级缓存。 用事务,MVCC,增量备份等替换RDBM ... 本地数据处理和过滤。 MapDB具有实用程序,可以在合理的时间内处理大量数据。 你好,世界 Maven代码段,...

    mark-sweep-simulation:标记清除垃圾收集器的仿真

    标记清扫垃圾收集器的仿真标记清除垃圾收集器的高级实现。 创建基于堆栈的VM的模型,其中堆栈保存对曾经分配的所有对象的引用。 维护一个freelist内存,从中进行所有分配。 可以配置的虚拟机参数为: GC调用的阈值-...

    深入java虚拟机

    1. JVM调优 1.1 JVM调优总结(一)-一些概念 1.2 JVM调优总结(二)-一些概念 1.3 JVM调优总结(三)-基本垃圾回收算法 1.4 JVM调优总结(四)-垃圾...4.2 JVM内存管理:深入垃圾收集器与内存分配策略 4.3 深入理解JVM

    resin-jvm 调优

    理解了应用程序的工作负荷和jvm支持的垃圾收集算法,便可以进行优化配置垃圾收集器。 垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是...

    Java-JVM优化视频.zip

    网盘文件永久链接 目录 day1: 1 为什么要对jvm做优化 2 jvm的运行参数 3 jvm的内存模型 ...3 垃圾收集器以及内存分配 4 可视化GC日志分析工具 day3: 1 Tomcat8优化 2 JVM字节码 3 代码优化 .........

    javascript垃圾收集机制与内存泄漏详细解析

    为此,垃圾收集器会按照固定的时间间隔(或代码执行中预设的收集时间),周期性的执行这一操作。 下面我们来分析一下函数中局部变量正常的生命周期。局部变量只在函数执行的过程中存在。而在这个过程中,

    java虚拟机精讲(电子工业出版社出版)

    本书以极其精练的语句诠释了 HotSpot VM的方方面面,比如:字节码的编译原理、字节码的内部组成结构、通过源码的方式剖析 HotSpot VM 的启动过程和初始化过程、Java 虚拟机的运行时内存、垃圾收集算法、垃圾收集器...

Global site tag (gtag.js) - Google Analytics