没有合适的资源?快使用搜索试试~ 我知道了~
介绍一下CMS,G1收集器。本文导线当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。1
资源详情
资源评论
资源推荐
常见面试题
问题答案在文中都有提到
如何判断对象是否死亡(两种方法)。
简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的
区别、使用软引用能带来的好处)。
如何判断一个常量是废弃常量
如何判断一个类是无用的类
垃圾收集有哪些算法,各自的特点?
HotSpot为什么要分为新生代和老年代?
常见的垃圾回收器有哪些?
介绍一下CMS,G1收集器。
MinorGc和FullGC有什么不同呢?
本文导线
当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对
这些“自动化”的技术实施必要的监控和调节。
1揭开JVM内存分配与回收的神秘面纱
Java的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java自动内存
管理最核心的功能是堆内存中对象的分配与回收。
Java堆是垃圾收集器管理的主要区域,因此也被称作GC堆(GarbageCollected
Heap)。从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以Java
堆还可以细分为:新生代和老年代:再细致一点有:Eden空间、FromSurvivor、To
Survivor空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。
堆空间的基本结构:
上图所示的eden区、s0("From")区、s1("To")区都属于新生代,tentired区属于老年
代。大部分情况,对象都会首先在Eden区域分配,在一次新生代垃圾回收后,如果对象还
存活,则会进入s1("To"),并且对象的年龄还会加1(Eden区->Survivor区后对象的初始
年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。对象
晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold来设置。
经过这次GC后,Eden区和"From"区已经被清空。这个时候,"From"和"To"会交换他们的
角色,也就是新的"To"就是上次GC前的“From”,新的"From"就是上次GC前的"To"。不
管怎样,都会保证名为To的Survivor区域是空的。MinorGC会一直重复这样的过程,直
到“To”区被填满,"To"区被填满之后,会将所有对象移动到老年代中。
1.1对象优先在eden区分配
目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代,这
样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
大多数情况下,对象在新生代中eden区分配。当eden区没有足够空间进行分配时,虚拟
机将发起一次MinorGC。下面我们来进行实际测试一下。
在测试之前我们先来看看MinorGC和FullGC有什么不同呢?
新生代GC(MinorGC):指发生新生代的的垃圾收集动作,MinorGC非常
频繁,回收速度一般也比较快。
老年代GC(MajorGC/FullGC):指发生在老年代的GC,出现了MajorGC
经常会伴随至少一次的MinorGC(并非绝对),MajorGC的速度一般会比Minor
GC的慢10倍以上。
针对HotSpotVM的实现,它里面的GC其实准确分类只有两大种:
PartialGC:并不收集整个GC堆的模式
YoungGC:只收集younggen的GC
OldGC:只收集oldgen的GC。只有CMS的concurrentcollection是这个模式
MixedGC:收集整个younggen以及部分oldgen的GC。只有G1有这个模式
FullGC:收集整个堆,包括younggen、oldgen、permgen(如果存在的话)等所有部分的模
式。
MajorGC通常是跟fullGC是等价的,收集整个GC堆。但因为HotSpotVM发展了这么多年,外界
对各种名词的解读已经完全混乱了,当有人说“majorGC”的时候一定要问清楚他想要指的是上
面的fullGC还是oldGC。
最简单的分代式GC策略,按HotSpotVM的serialGC的实现来看,触发条件是:
youngGC:当younggen中的eden区分配满的时候触发。注意youngGC中有部分存活对象会晋
升到oldgen,所以youngGC后oldgen的占用量通常会有所升高。
fullGC:当准备要触发一次youngGC时,如果发现统计数据说之前youngGC的平均晋升大小比
目前oldgen剩余的空间大,则不会触发youngGC而是转为触发fullGC(因为HotSpotVM的GC
里,除了CMS的concurrentcollection之外,其它能收集oldgen的GC都会同时收集整个GC堆,
包括younggen,所以不需要事先触发一次单独的youngGC);或者,如果有permgen的话,
要在permgen分配空间但已经没有足够空间时,也要触发一次fullGC;或者System.gc()、heap
dump带GC,默认也是触发fullGC。
HotSpotVM里其它非并发GC的触发条件复杂一些,不过大致的原理与上面说的其实一样。
当然也总有例外。ParallelScavenge(-XX:+UseParallelGC)框架下,默认是在要触发fullGC前
先执行一次youngGC,并且两次GC之间能让应用程序稍微运行一小下,以期降低fullGC的暂停
时间(因为youngGC会尽量清理了younggen的死对象,减少了fullGC的工作量)。控制这个行
为的VM参数是-XX:+ScavengeBeforeFullGC。这是HotSpotVM里的奇葩嗯。可跳传送门围观:
JVMfullGC的奇怪现象,求解惑?-RednaxelaFX的回答
并发GC的触发条件就不太一样。以CMSGC为例,它主要是定时去检查oldgen的使用量,当使用
量超过了触发比例就会启动一次CMSGC,对oldgen做并发收集。
测试:
1 publicclassGCTest{
2 publicstaticvoidmain(String[]args){
3 byte[]allocation1,allocation2;
4 allocation1=newbyte[30900*1024];
5 //allocation2=newbyte[900*1024];
6 }
7 }
通过以下方式运行:
添加的参数:-XX:+PrintGCDetails
运行结果(元空间描述有误,应该是对应于JDK1.7的永久代):
从上图我们可以看出eden区内存几乎已经被分配完全(即使程序什么也不做,新生代也会
使用2000多k内存)。假如我们再为allocation2分配内存会出现什么情况呢?
1 allocation2=newbyte[900*1024];
简单解释一下为什么会出现这种情况:因为给allocation2分配内存的时候eden区内存
几乎已经被分配完了,我们刚刚讲了当Eden区没有足够空间进行分配时,虚拟机将发起一
次MinorGC。GC期间虚拟机又发现allocation1无法存入Survivor空间,所以只好通
过分配担保机制把新生代的对象提前转移到老年代中去,老年代上的空间足够存放
allocation1,所以不会出现FullGC。执行MinorGC后,后面分配的对象如果能够存在
eden区的话,还是会在eden区分配内存。可以执行如下代码验证:
1 publicclassGCTest{
2 publicstaticvoidmain(String[]args){
3 byte[]allocation1,allocation2,allocation3,allocation4,allocation5;
4 allocation1=newbyte[32000*1024];
5 allocation2=newbyte[1000*1024];
6 allocation3=newbyte[1000*1024];
7 allocation4=newbyte[1000*1024];
8 allocation5=newbyte[1000*1024];
9 }
10 }
1.2大对象直接进入老年代
大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。
为什么要这样呢?
为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。
1.3长期存活的对象将进入老年代
既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放
在新生代,哪些对象应放在老年代中。为了做到这一点,虚拟机给每个对象一个对象年龄
(Age)计数器。
如果对象在Eden出生并经过第一次MinorGC后仍然能够存活,并且能被Survivor容纳
的话,将被移动到Survivor空间中,并将对象年龄设为1。对象在Survivor中每熬过一次
MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就会被晋升
到老年代中。对象晋升到老年代的年龄阈值,可以通过参数-
XX:MaxTenuringThreshold来设置。
1.4动态对象年龄判定
大部分情况,对象都会首先在Eden区域分配,在一次新生代垃圾回收后,如果对象还存
活,则会进入s0或者s1,并且对象的年龄还会加1(Eden区->Survivor区后对象的初始
年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。对象
晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold来设置。
“Hotspot遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大
小超过了survivor区的一半时,取这个年龄和MaxTenuringThreshold中更小的一个值,作为新的
晋升年龄阈值”。
动态年龄计算的代码如下:
uintageTable::compute_tenuring_threshold(size_tsurvivor_capacity){
//survivor_capacity是survivor空间的大小
size_tdesired_survivor_size=(size_t)((((double)
survivor_capacity)*TargetSurvivorRatio)/100);
size_ttotal=0;
uintage=1;
while(age<table_size){
total+=sizes[age];//sizes数组是每个年龄段对象大小
if(total>desired_survivor_size)break;
剩余36页未读,继续阅读
李多田
- 粉丝: 67
- 资源: 334
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0