如何理解jvm的jvm eden space和survivor区,以及gc

&&&&&& JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见 &&&&&& 现在比较常用的是分代收集(generational collection,也是SUN VM使用的,J2SE1.2之后引入),即将内存分为几个区域,将不同生命周期的对象放在不同区域里:young generation,tenured generation和permanet generation。绝大部分的objec被分配在young generation(生命周期短),并且大部分的object在这里die。当young generation满了之后,将引发minor collection(YGC)。在minor collection后存活的object会被移动到tenured generation(生命周期比较长)。最后,tenured generation满之后触发major collection。major collection(Full gc)会触发整个heap的回收,包括回收young generation。permanet generation区域比较稳定,主要存放classloader信息。 &&&&&& young generation有eden、2个survivor 区域组成。其中一个survivor区域一直是空的,是eden区域和另一个survivor区域在下一次copy collection后活着的objecy的目的地。object在survivo区域被复制直到转移到tenured区。
&&&&&& 我们要尽量减少 Full gc 的次数(tenured generation 一般比较大,收集的时间较长,频繁的Full gc会导致应用的性能收到严重的影响)。
堆内存GC&&&&&& JVM(采用分代回收的策略),用较高的频率对年轻的对象(young generation)进行YGC,而对老对象(tenured generation)较少(tenured generation 满了后才进行)进行Full GC。这样就不需要每次GC都将内存中所有对象都检查一遍。
非堆内存不GC
&&&&& GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS(特别是动态生成类,当然permgen space存放的内容不仅限于类)的话,就很可能出现PermGen Space错误。
内存申请、对象衰老过程一、内存申请过程
JVM会试图为相关Java对象在Eden中初始化一块内存区域;
当Eden空间足够时,内存申请结束。否则到下一步;
JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
Survivor区被用来作为Eden及old的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
当old区空间不够时,JVM会在old区进行major collection;
完全垃圾收集后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现"Out of memory错误"; 二、对象衰老过程
新创建的对象的内存都分配自eden。Minor collection的过程就是将eden和在用survivor space中的活对象copy到空闲survivor space中。对象在young generation里经历了一定次数(可以通过参数配置)的minor collection后,就会被移到old generation中,称为tenuring。
GC触发条件
GC类型 触发条件 触发时发生了什么 注意 查看方式
YGC eden空间不足
清空Eden+from survivor中所有no ref的对象占用的内存将eden+from sur中所有存活的对象copy到to sur中一些对象将晋升到old中:&&& to sur放不下的&&& 存活次数超过turning threshold中的重新计算tenuring threshold(serial parallel GC会触发此项) 重新调整Eden 和from的大小(parallel GC会触发此项) 全过程暂停应用是否为多线程处理由具体的GC决定 jstat –gcutil gc log
old空间不足perm空间不足显示调用System.GC, RMI等的定时触发YGC时的悲观策略dump live的内存信息时(jmap –dump:live) 清空heap中no ref的对象permgen中已经被卸载的classloader中加载的class信息如配置了CollectGenOFirst,则先触发YGC(针对serial GC)如配置了ScavengeBeforeFullGC,则先触发YGC(针对serial GC) 全过程暂停应用是否为多线程处理由具体的GC决定是否压缩需要看配置的具体GC jstat –gcutil gc logpermanent generation空间不足会引发Full GC,仍然不够会引发PermGen Space错误。 参考:
& 相关内容推荐:
阅读(...) 评论()本文是一次内部分享中总结了jvm gc的分类和一些实例, 内容是introduction级别的,供初学人士参考.
成文仓促,难免有些错误,如果有大牛发现,请留言,我一定及时更正,谢谢!
JVM内存布局主要包含下面几个部分:
Java Virtual Machine Stack: 也就是我们常见的局部变量栈,线程私有,保存线程执行的局部变量表、操作栈、动态连接等。
Java Heap:我们最常打交道的内存区域,几乎所有对象的实例都在这个区域分配。所谓的GC基本上也就是跟这个区域打交道。
Method Area:包含被虚拟机加载的类、常量、静态变量等数据。
Hotspot虚拟机使用分代收集算法,将Java Heap根据对象的存活周期分为多个区域:新生代、老生代和永生代。
新生代和老生代位于Java heap中,是垃圾收集器主要处理的内存区域。
永生代则基本上等价于Method Area,也就是说其中包含的数据在jvm进程存活期间会一直存在,一般不会发生变化。
java堆内存的布局如下图所示:
使用jstat可以查看某个java进程的内存状况:
chendeMacBook-Air:~ eleforest$ jstat -gc 16136
其中各个指标介绍如下:(单位为KB)
S0C,S1C,S0U,S1U: 0/1幸存区(survivor)容量(C:Capacity)/使用量(U:Used)。
EC,EU: Eden(伊甸)区容量/用量。Eden和survivor两个区域位于新生代,由于新生代GC一般是使用复制算法进行清理,因此按照复制算法的原理将新生代分成了3个区域:Eden、Survivor0、Survivor1。Hotspot虚拟机的3个空间缺省配比为:8:1:1,jvm只会使用eden和1个survivor作为新生代空间.当新生代空间不足时发生minor gc,此时根据复制算法, jvm会首先 1)将eden和from survivor中存活的对象拷贝到to survior中,然后2)释放eden和from中的所有需要回收对象,最后3)调换from/to survior,jvm将eden和新的from survior作为新生代。当然上述minor gc顺利执行还取决于很多因素,这里只描述了最理想化的状态。
OC,OU: Old(老生代)容量/用量。老生代常用的垃圾收集器有CMS、Serial Old、Parallel Old等
PC,PU: Perm(永生代)容量/用量。
YGC/YGCT: Young GC次数和总耗费时间。Young GC也就是Minor GC,新生代中内存不够时触发,通常采用复制算法进行,回收速度较快,对系统的影响较小。
FGC/FGCT:Full GC次数和总耗费时间。Full GC是在java heap空间不足(包括New和Old区域)时触发,会分别清理新生代、老生代,通常耗时较长,对系统有较大影响,应该尽量避免。
GCT:GC总耗时。
常用的垃圾收集器包括下面几个
Serial:最基本,历史最悠久的收集器,单线程收集垃圾内存,在新生代采用复制算法,在老生代使用标记-整理算法
ParNew:Serial的多线程版本,主要用于新生代收集。与CMS收集器配合成为现在最常用的server收集器
Parallel Scavenge:也是一个并行收集器,使用与ParNew完全不同的收集策略,具体的差别还在研究中
CMS:Concurrent Mark Sweep收集器,大名鼎鼎,其目标是获取最短回收停顿时间,是server模式下最常用的收集器
G1:最新的收集器,木有用过啊
下面将会用一段简单的程序演示jvm在配置使用不同的收集器情况下,GC行为的不同点,通过GC的行为能够了解到不同收集器的收集策略和行为。代码非常简单:
//jvm basic args:-Xmx20M -Xms20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
public class Main {
public static void main(String[] args) throws Exception {
byte[] alloc1,alloc2,alloc3,alloc4;
alloc1 = new byte[2*];
Thread.sleep(2000);
alloc2 = new byte[2*];
Thread.sleep(2000);
alloc3 = new byte[2*];
Thread.sleep(2000);
alloc4 = new byte[2*];
Thread.sleep(2000);
其中上例中的jvm参数解释如下:
最大堆容量,包含了新生代和老生代的堆容量
最小堆容量,此时配置与Xmx一样,避免了申请空间时的堆扩展
新生代容量,包含eden,survivor1,survivor2三个区域
PrintGCDetails
让jvm在每次发生gc的时候打印日志,利于分析gc的原因和状况
SurvivorRatio
新生代中eden的比例,如果设置为8,意味着新生代中eden占据80%的空间,两个survivor分别占据10%
测试环境为mac os 10.8,jdk版本如下:
chendeMacBook-Air:~ eleforest$ java -version
java version &1.7.0_09&
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)
示例1:让jvm自动选择收集器
直接运行上述代码,用jstat观察gc情况如下:
chendeMacBook-Air:~ eleforest$ jstat -gc
由上述的结果可见,程序启动时,Eden使用了819.9K的空间(我现在还不知道819k是什么东西的开销),S1、S2、老生代均没有占用,永生代则使用了2.6MB空间,其中包含了包含被虚拟机加载的类、常量、静态变量等数据。
随后连续三次申请了2MB的空间,这些数据都被放到了Eden区域,这就是jvm内存分配的第一个原则:对象优先在Eden分配,这个原则只在Eden空间足够,且申请的内存小于jvm参数PretenureSizeThreshold设置值时生效(根据采用的收集器不同,还会有很多不同情况)
注意看第四次申请2MB空间,此时由于Eden空间无法容纳新的数组,因此发生了一次Minor GC,具体的GC log如下所示:
[GC [DefNew: 6963K-&292K(9216K), 0.0065350 secs] 6963K-&K), 0.0065940 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
def new generation
total 9216K, used 2832K [0x00, 0xc30000)
eden space 8192K,
31% used [0xaaf60, 0xa30000)
from space 1024K,
28% used [0xb3b0, 0xc30000)
space 1024K,
0% used [0xa300, 0xb30000)
tenured generation
total 10240K, used 6144K [0xc30, 0x0000)
the space 10240K,
60% used [0xc30, 0x0)
compacting perm gen
total 21248K, used 2647K [0x00, 0x0000)
the space 21248K,
12% used [0xec0, 0xc00)
No shared spaces configured.
其中第一行中的"DefNew"代表使用的收集器是Serial收集器,这次Minor GC使用copy算法,做了下面几件事情:
检索heap中的对象,将还能通过GC roots能够遍历到的对象copy到to区中
如果需要copy的对象没法进入from区中,则将其晋升到老年代,本例中即发生了这种情况,3个2MB的数组全部晋升到老生代(OU:6144)
清理eden和from中无用的垃圾
互换from和to空间
比较有意思的是,在我的机器上重新再跑一次示例程序,发生了不一致的gc行为:
[GC [PSYoungGen: 6963K-&384K(9216K)] 6963K-&K), 0.0052500 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[Full GC [PSYoungGen: 384K-&0K(9216K)] [ParOldGen: 6144K-&K)] 6528K-&K) [PSPermGen: 2637K-&K)], 0.0157270 secs] [Times: user=0.04 sys=0.00, real=0.02 secs]
PSYoungGen
total 9216K, used 2539K [0xd0, 0xd0000)
eden space 8192K, 31% used [0xdaf60,0xed0000)
from space 1024K, 0% used [0xed00,0xfd0000)
space 1024K, 0% used [0xfd00,0xd0000)
total 10240K, used 6436K [0xfcd0, 0xd0000)
object space 10240K, 62% used [0xfcd8,0xd0000)
total 21248K, used 2645K [0xaad000, 0xfcd0000)
object space 21248K, 12% used [0xaad688,0xbf90000)
GC log第一行的PSYoungGen意味着这次运行中jvm自动选择了Parallel Scavenge收集器,GC行为发生了变化,同样的内存请求,PS收集器除了一次Minor GC以外,还发生了一次Full GC。PS收集器的实现与serial不一致,其行为模式还需要进一步研究.
比较吊诡的是jvm的自动选择行为,我阅读了openjdk的源码,版本为:openjdk-7-fcs-src-b147-27_jun_2011
其中关于jvm自动选择gc的代码如下:
if (os::is_server_class_machine() && !force_client_mode ) {
// If no other collector is requested explicitly,
// let the VM select the collector based on
// machine class and automatic selection policy.
if (!UseSerialGC &&
!UseConcMarkSweepGC &&
!UseG1GC &&
!UseParNewGC &&
!DumpSharedSpaces &&
FLAG_IS_DEFAULT(UseParallelGC)) {
if (should_auto_select_low_pause_collector()) {//如果需要低时延收集器,选择cms
FLAG_SET_ERGO(bool, UseConcMarkSweepGC, true);
} else {//否则缺省使用ps收集器
FLAG_SET_ERGO(bool, UseParallelGC, true);
no_shared_spaces();
如上所示,jvm在没有明确设置gc时会采用parallel scavenge作为缺省收集器。因此我机器上jvm自动选择gc的行为还需要进一步研究。
示例2:使用ParNew收集器
调整jvm的参数,添加-XX:+UseParNewGC,告诉jvm选择使用ParNew收集器,此时执行的结果与示例1中使用serial收集器的行为完全一样。这里不再赘述
示例3:使用CMS收集器
调整jvm参数为:
-Xmx20M -Xms20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC&/pre&
此时启动示例程序,我们会看到如下的结果:
&pre class=&brush:shell&&chendeMacBook-Air:~ eleforest$ jstat -gc
也就是说到第四个2MB申请,老生代里使用6MB的数据之后,jvm还进行了6次full gc,这是由于cms特殊性导致的:cms为了保证进行gc时应用的低时延,要求在老生代中剩余充足的空间以备应用使用。这个特性可以用下列参数进行调整和限制
-XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly
其中CMSInitiatingOccupancyFraction的缺省为68%。在我们的示例中,OU已经超过了这个限制,jvm试图去清理老生代,因此发生了多次full gc。
通过修改CMSInitiatingOccupancyFraction为80或者更高值,再次执行示例程序后不会再发生fullGC。
为了使应用平顺,CMS收集器的使用需要小心的调整堆空间的大小,太小的老生代可能会起到相反的效果,过高的CMSInitiatingOccupancyFraction也会导致回收数据时使应用无法正常工作。
以上便是我在这篇博客中想要分享的内容,做一些记录,也分享出来。
但是如分享中所说的,还有以下问题还没有搞清楚:
PS收集器的行为,触发full gc的条件
jvm自动选择收集器的策略
G1收集器的使用
“其中CMSInitiatingOccupancyFraction的缺省为68%”
上面这句话建议考究下,我记得jdk1.6这样的普及版本,默认值是92%,一些书籍或者网上资料都写了是68%,这个应该是历史版本。如果按照68%去配置相关参数,可能会导致进入老年代的空间不足引发fullgc,这个cms的fullgc是个单线程回收,还是比较恐怖的。
A quality product byJava GC系列(1):Java垃圾回收简介 - ImportNew
| 分类: ,
| 标签: ,
Java的内存分配与回收全部由JVM垃圾回收进程自动完成。与C语言不同,Java开发者不需要自己编写代码实现垃圾回收。这是Java深受大家欢迎的众多特性之一,能够帮助程序员更好地编写Java程序。
下面四篇教程是了解Java 垃圾回收(GC)的基础:
这篇教程是系列第一部分。首先会解释基本的术语,比如JDK、JVM、JRE和HotSpotVM。接着会介绍JVM结构和Java 堆内存结构。理解这些基础对于理解后面的垃圾回收知识很重要。
Java关键术语
JavaAPI:一系列帮助开发者创建Java应用程序的封装好的库。
Java 开发工具包 (JDK):一系列工具帮助开发者创建Java应用程序。JDK包含工具编译、运行、打包、分发和监视Java应用程序。
Java 虚拟机(JVM):JVM是一个抽象的计算机结构。Java程序根据JVM的特性编写。JVM针对特定于操作系统并且可以将Java指令翻译成底层系统的指令并执行。JVM确保了Java的平台无关性。
Java 运行环境(JRE):JRE包含JVM实现和Java API。
Java HotSpot 虚拟机
每种JVM实现可能采用不同的方法实现垃圾回收机制。在收购SUN之前,Oracle使用的是JRockit JVM,收购之后使用HotSpot JVM。目前Oracle拥有两种JVM实现并且一段时间后两个JVM实现会合二为一。
HotSpot JVM是目前Oracle SE平台标准核心组件的一部分。在这篇垃圾回收教程中,我们将会了解基于HotSpot虚拟机的垃圾回收原则。
JVM体系结构
下面图片总结了JVM的关键组件。在JVM体系结构中,与垃圾回收相关的两个主要组件是堆内存和垃圾回收器。堆内存是内存数据区,用来保存运行时的对象实例。垃圾回收器也会在这里操作。现在我们知道这些组件是如何在框架中工作的。
Java堆内存
我们有必要了解堆内存在JVM内存模型的角色。在运行时,Java的实例被存放在堆内存区域。当一个对象不再被引用时,满足条件就会从堆内存移除。在垃圾回收进程中,这些对象将会从堆内存移除并且内存空间被回收。堆内存以下三个主要区域:
新生代(Young Generation)
Eden空间(Eden space,任何实例都通过Eden空间进入运行时内存区域)
S0 Survivor空间(S0 Survivor space,存在时间长的实例将会从Eden空间移动到S0 Survivor空间)
S1 Survivor空间 (存在时间更长的实例将会从S0 Survivor空间移动到S1 Survivor空间)
老年代(Old Generation)实例将从S1提升到Tenured(终身代)
永久代(Permanent Generation)包含类、方法等细节的元信息
永久代空间中已经被移除。
在本系列的第二篇将会介绍。
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
(新浪微博:好好先生耶2)
微信关注: ImportNew
分享Java相关的技术文章、工具资源和热点资讯。扫描加关注,碎片时间提高Java开发技能!
第四个的第三个类写错了,是collections,少了s
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
新浪微博:
微信号:importnew
反馈建议:@
广告与商务合作QQ:
& 2015 ImportNewJVM内存管理_GC模型_编写GC友好的代码_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
文档贡献者
评价文档:
喜欢此文档的还喜欢
JVM内存管理_GC模型_编写GC友好的代码
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
大小:1.18MB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢3.6.3 长期存活的对象将进入老年代
本文所属图书&>&
本书第1版两年内印刷近10次,4家网上书店的评论近4?000条,98%以上的评论全部为5星级的好评,是整个Java图书领域公认的经典著作和超级畅销书,繁体版在台湾也十分受欢迎。第2版在第1版的基础上做了很大的改进:根...&&
既然采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这点,给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。对象在Survivor区中每&熬过&一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。
读者可以试试分别以-XX:MaxTenuringThreshold=1和-XX:MaxTenuringThreshold=15两种设置来执行代码清单3-7中的testTenuringThreshold()方法,此方法中的allocation1对象需要256KB内存,Survivor空间可以容纳。当MaxTenuringThreshold=1时,allocation1对象在第二次GC发生时进入老年代,新生代已使用的内存GC后非常干净地变成0KB。而MaxTenuringThreshold=15时,第二次GC发生后,allocation1对象则还留在新生代Survivor空间,这时新生代仍然有404KB被占用。
代码清单3-7 长期存活的对象进入老年代
private static final int _1MB = 1024 * 1024;
&* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1
&* -XX:+PrintTenuringDistribution
@SuppressWarnings(&unused&)
public static void testTenuringThreshold() {
& byte[] allocation1, allocation2, allocation3;
& allocation1 = new byte[_1MB / 4];&
&& // 什么时候进入老年代取决于XX:MaxTenuringThreshold设置
& allocation2 = new byte[4 * _1MB];
& allocation3 = new byte[4 * _1MB];
& allocation3 =
& allocation3 = new byte[4 * _1MB];
以MaxTenuringThreshold=1参数来运行的结果:
[GC [DefNew
Desired Survivor size 524288 bytes, new threshold 1 (max 1)
- age&& 1:&&&& 414664 bytes,&&&& 414664 total
: 4859K-&404K(9216K), 0.0065012 secs] 4859K-&K), 0.0065283 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
[GC [DefNew
Desired Survivor size 524288 bytes, new threshold 1 (max 1)
: 4500K-&0K(9216K), 0.0009253 secs] 8596K-&K), 0.0009458 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
&def new generation&& total 9216K, used 4178K [0x029ddd0000)
& eden space 8192K,& 51% used [0x029dded0000)
& from space 1024K,&& 0% used [0x031ddd0000)
& to&& space 1024K,&& 0% used [0x032ddd0000)
&tenured generation&& total 10240K, used 4500K [0x033ddddd0000)
&& the space 10240K,& 43% used [0x033d33dd0000)
&compacting perm gen& total 12288K, used 2114K [0x03ddddd0000)
&& the space 12288K,& 17% used [0x03ddfefe0a00, 0x049d0000)
No shared spaces configured.
以MaxTenuringThreshold=15参数来运行的结果:
[GC [DefNew
Desired Survivor size 524288 bytes, new threshold 15 (max 15)
- age&& 1:&&&& 414664 bytes,&&&& 414664 total
: 4859K-&404K(9216K), 0.0049637 secs] 4859K-&K), 0.0049932 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [DefNew
Desired Survivor size 524288 bytes, new threshold 15 (max 15)
- age&& 2:&&&& 414520 bytes,&&&& 414520 total
: 4500K-&404K(9216K), 0.0008091 secs] 8596K-&K), 0.0008305 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
&def new generation&& total 9216K, used 4582K [0x029ddd0000)
& eden space 8192K,& 51% used [0x029dded0000)
& from space 1024K,& 39% used [0x031d3d0000)
& to&& space 1024K,&& 0% used [0x032ddd0000)
&tenured generation&& total 10240K, used 4096K [0x033ddddd0000)
&& the space 10240K,& 40% used [0x033ddddd0000)
&compacting perm gen& total 12288K, used 2114K [0x03ddddd0000)
&& the space 12288K,& 17% used [0x03ddfefe0a00, 0x049d0000)
No shared spaces configured.
您对本文章有什么意见或着疑问吗?请到您的关注和建议是我们前行的参考和动力&&
您的浏览器不支持嵌入式框架,或者当前配置为不显示嵌入式框架。

我要回帖

更多关于 jvm gc原理 的文章

 

随机推荐