- 垃级回收面对的三个问题
-
- 哪些内存需要被回收
- 什么时候回收
- 如何回收
- 程序计数器、虚拟机栈、本地方法栈随着线程而生,随首线程而亡。栈中的栈帧随着方法的进入和退出而有条不紊的执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的。因此这几个区域的内存分配和回收都具备确定性,这几个区域内就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而JAVA堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期内才会知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的是这部分内存。(即垃圾回收器主要关注堆的内存回收)
对象是否存活判断 |
1.引用计数法
/**
* 循环引用测试
* testGC()方法执行后,objA和objB会不会被GC呢?
* 会的
*
*
* @author zzm
*/
public class _9ReferenceCountingGC {
public Object instance = null;
public static void main(String[] args) {
_9ReferenceCountingGC objA = new _9ReferenceCountingGC();
_9ReferenceCountingGC objB = new _9ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA=null;
objB=null;
/**
* 假设在这行发生GC,objA和objB是否能被回收?
*
会被回收!循环引用会被回收!
* java没有使用引用计数器
* 如果是引用计数器那么不会被回收
*
*/
System.gc();
}
}
-XX:+PrintGCDetails 可以查看GC日志
|
2.可达性分析算法
-Xnoclassgc 关闭CLASS的垃圾回收功能 -verbose:class 在程序运行的时候有多少类被加载!你可以用verbose:class来监视 -XX:+TraceClassLoading XX:+TraceClassUnLoading –监控类的加载/卸载
|
垃圾收集算法
|
2.复制算法
|
3.标记-整理算法
|
4.分代收集算法
|
HotSpot的算法实现
枚举根节点
从可达性分析中从GC Roots节点找引用链这个操作为例,
可作为GC Roots的节点主要在全局性的引用(常量池或类列态属性)与执行上下文(栈帧中本地变量表),现在很多应用仅仅方法区就有数百兆,如果要逐个检查这里面的引用,那么必然会消耗很多时间。
另外,
可达性分析对执行时间的敏感还休现在GC停顿上,因为这项分析工作必须在一个能确保一致性的快照中进行—这里『一致性』的快照中进行,
不可以出现分析过程中对象引用关系还在不断变化的情况。
这点是导致GC进行时必须停顿所有的JAVA执行线程(SUN 将这件事情称之为 Stop The World),即使是在号称(几乎)不会发生停顿的CMS收集器中,枚举根节点也是几须要停顿的。
目前主流JAVA虚拟机使用的都是
准确式GC,所以当执行系统停顿下来后,并不需要一个不漏的检查完所有执行上下文和全局的引用位置,虚拟机应当是有办法直接得知哪些地方存放着对象引用。在HotSpot中,使用一组称为OopMap的数据结构来达到这个目的的,在类加载完成的时候HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样GC在扫描时就可以直接得知这些信息了。
|
安全点
在OopMap的协助下,HotSpot可以快速且准确地完成GC Roots枚举,但一个很现实的问题随这而来:可能导致引用关系变化,或者说OopMap内容变化指令非常多,如果为每个指令都生成对应的OopMap,将会消耗很多额外空间,这样GC成本会变高。
HopSpot没有为每条指令都生成OopMap,只在安全点记录了这些信息,即程序
执行时并非在所有地方都能停顿下来开妈GC,只有在到达安全点时才能暂停。
安全点的选定基本是以『程序是否有让程序长时间执行的特征』为标准进行选定的。
如何上所有线程跑到最近的安全点上再停顿下来?有两种方案:
抢先式中段和主动式中断。
|
安全区域
SafePoint机制保证了程序执行时,在不太长的时间内就会遇到可进入GC的安全点中。但是程序不执行,如sleep或者block了,那么这时候线程无法响应JVM中断请求,走到安全点。此时需要安全区域
安全区域指引用关系不会发生变化。这这个区域中的任意地方开始GC 都是安全的。
在线程执行到安全区域的时候,首先标识自己进入了safe region那么,当在这段时间里JVM 要发起GC 时,就不用管标识自己把自己标识为SafeRegion状态的线程了。在线程要离开safeRegion时,它要检查系统是否已经完成了对根节点枚举,或者是整个GC过程,如果完成了,那线程继续执行,否则它就必须等待直到收到可以安全离开safe region的信号为止
|
垃圾收集器
比
有连线的可以搭配使用
CMS — Concurrent Mark Sweep
|
分代 | No | 名称 | 优点 | 缺点 | 开启参数 | 算法 | 使用场景 | since version | 备注 |
---|---|---|---|---|---|---|---|---|---|
新生代 | 1 | Serial | 简单高效 | stop the world | -XX:+UseSerialGC | 单线程、新生代-复制算法 老生代-标记整理 | 内存较小的client模式下首选的新生代垃圾收集器 | jdk1 | 无 |
新生代 | 2 | ParNew | 多cpu环境下,比Serial效果好 | stop the world、cpu核数少时线程切换的开销使得没有serial性能好 | -XX:+UseParNewGC或 -XX:+UseConcMarkSweepGC | 多线程并行、复制算法 | server首选、jdk6之前,老年代用CMS,新生代只能用Serial或ParNew | ? | 除了serial只有它可以与CMS配合工作 |
新生代 | 3 | Parallel Scavenge或称throughput收集器 | 高吞吐量、带有GC Ergonomics(gc自适应调节策略,使用-XX:+UseAdaptiveSizePolicy开启),无需手工指定新生代大小,survivor比例,晋升老年代对象的年龄等参数。 | stop the world;不适合响应时间要求高/交互频繁的应用 | ‘-XX:+UseParallelGC或-XX:+UseParallelOldGC | 多线程、复制算法 | 关注高吞吐量并且响应性要求不高的应用 | 可以自动优化 | UseParallelGC时老年代为SerialOldGC,UseParallelOldGC时老年代为ParallelOldGC |
老年代 | 4 | Serial Old | 简单高效 | stop the world | ‘-XX:+UseSerialGC或’-XX:+UseParNewGC | 单线程、标记整理 | client首选,搭配ParNew使用,或者在jdk6之前搭配Parallel Scavenge使用,也可在server模式下作为CMS的备胎(CMS可能会因为浮动垃圾而发生concurrent mode failure的错误,此时需要serial old上位) | ? | 无 |
老年代 | 5 | Parallel Old | 高吞吐量 | stop the world,不适合响应时间要求高的应用 | -XX:+UseParallelOldGC | 多线程并行,标记整理 | server模式默认选项。搭配Parallel Scavenge,关注吞吐量及cpu资源敏感的场合 | jdk6 | 该选项自动设置 -XX:+UseParallelGC选项 |
老年代 | 6 | CMS | 停顿时间短,响应性高 | 对cpu资源非常敏感;无法处理浮动垃圾,可能会出现Concurrent mode failure;标记清除算法容易产生垃圾碎片 | -XX:+UseConcMarkSweepGC | 多线程并发,标记清除 | 要求高响应性的互联网站或BS服务端 | jdk5 | 打开此开关参数后,使用ParNew+CMS+Serial Old收集器组 |
老年代 | 7 | G1 | 当内存很大时,停顿时间高,并且吞吐量高 | 内存较小时性能没有CMS好 | -XX:+UseG1GC | 使用空间整合算法,堆被划分成多个连续的大小相等的region,新生代老年代物理上不再隔离,新生代老年代各自在内部也不再要求连续。 | 内存大于6G的高并发低停顿应用 | jdk7 | 可能会在java9时作为默认GC |
Serial收集器
Serial垃圾收集器,通过这个单词的意思“连续”,那么必然不会采用“标记清除算法”来实现,所以这个垃圾收集器在
新生代使用的是“复制算法”,而
Serial Old作为Serail收集器的老年代版本,使用的就是“标记-整理算法”。
收集器是最基本、历史最悠久的收集器,在JDK1.3.1之前,是JVM新生代收集的唯一选择。
Serial收集器是一个单线程的收集器,这个“单线程”是指JVM在使用它进行GC的时候,必须暂停其他所有的工作线程(sun将这件事情称为“Stop the world”),直到GC完成,这是一件非常可怕的事情。看到这里,你可能想我一定要修改我的JVM的新生代收集器,不用Serial了,但是直至现在,Serial依然是JVM在运行Client模式下默认的新生代 收集器。与其他垃圾收集器的单线程相比,Serial简单而高效。
对于用户桌面应用场景来说,分配给JVM的内存一般不会太大,收集十几甚至一两百兆的内存,停顿时间可以控制在几十毫秒,最多一百多毫秒以内,只要不是特别频繁,这些停顿还是可以接受的。所以,对于
Client模式下的JVM来说,Serial是个很好的新生代收集器,
简单高效。
|
ParNew收集器
ParNew收集器也是一个新生代收集器,其实就是Serial收集器的多线程版本,是一个“并行”的垃圾收集器,除了多线程外,其他和Serial差不多。想想也就明白了,当JVM团队开发出来了Serial,可以满足Client模式下的JVM,但是对于Server模式下的JVM来说,运行很长时间,有很多的对象需要收集(可能几十个G),单线程导致的停顿时间太长了(比如每运行1小时需要停顿5分钟),用户无法接受业务线程停顿那么长的时间,我猜测这种情况下那些大牛能想到的最简单的办法就是让Serial变成多线程,这样开多个线程就可以有效的降低停顿时间,故而这个Serial的多线程版本也就诞生了。 ParNew是许多运行在Server模式下的JVM中首选的垃圾收集器,其中一个重要原因就是除了Serial,它是唯一可以和CMS(Concurrent Mark Sweep)老年代垃圾收集器配合工作。 ParNew在单CPU环境中收集效果不如Serial收集器,但是随着CPU的增加,它对于GC时系统资源的利用还是很有好处的,默认开启的线程数与CPU的数量一致 。 |
Parallel Scavenge收集器 平行扫描
|
Serial old收集器
|
Parallel old收集器
|
CMS (Concurrent Mark Sweep 同时标记清除)收集器
初始标记、重新标记仍然要Stop the world。初始标记仅仅是标记一下GC ROOTS 能直接关联到的对象,速度很快,并发标记除段就是进行GC ROOTS Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部份对象的标记记录,这个阶段的停顿会比初始标记阶段稍长一些,但远比并发标记时间短。
整个过程中耗时最长的并发标记和并发清除过程收集器线程都是可以与用户线程一起工作,所以,从总体上 来说CMS收集器的内存回收过程与用户线程一起并发执行。
|
G1(Garbage-First)收集器
实验看下来 G1 只有一个Survivor区?
在G1之前每种收集器只能收集新生代或老年代,
而G1可以收集老年代和新生成,G1会把JAVA堆划分为多个大小相等的独立区域,虽然还保留有新生代和老年代的概念,但新生代和老年不再是特理隔离,它们都是一部份区域的集合。
G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整 个JAVA堆中进行全区域的垃圾收集(不懂啥意思)。G1跟踪名个区域里面的垃圾堆积的价值大小(加收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,,优先回收价值最大的Region(也就是Garbage-First)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率。
在
以前的代分收集中,新生代的规模一般老年代要小许多,新生代收集比老年代也要频繁。
G1中Region之间的对象引用以及期他收集器中新生工与老年代之间的对象引用,虚拟机都是使用Remembered Set来买吧这名全堆扫描的。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(在分代的例子中就是栓查是否老年代中的对象引用了新生代中的对象),如果是,便通过CardTable把相妆引用信息记录到被引用对象所属的Region的remembered set之中,当进行内存回收时,在GC根节点的枚举范围中另入 Remembered set 即可保证不对全堆扫描也会不会有贵漏(不理解。。。。。)。
|
GC日志
[
GC
[DefNew: 4928K->512K(4928K), 0.0038992 secs] 6592K->2884K(15872K), 0.0039332 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [DefNew: 4928K->286K(4928K), 0.0026634 secs] 7300K->3379K(15872K), 0.0026951 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [DefNew: 4702K->335K(4928K), 0.0030641 secs] 7795K->3714K(15872K), 0.0030949 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System) [Tenured: 3378K->3897K(10944K), 0.0177885 secs] 5632K->3897K(15872K), [Perm : 2308K->2308K(12288K)], 0.0178426 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
[Full GC (System) [Tenured: 3897K->3858K(10944K), 0.0165858 secs] 4020K->3858K(15936K), [Perm : 2308K->2308K(12288K)], 0.0166245 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
[Full GC (System) [Tenured: 3858K->3866K(10944K), 0.0169109 secs] 3962K->3866K(15936K), [Perm : 2308K->2308K(12288K)], 0.0169604 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
|
常用垃圾收集器参数
-XX:+UseSerialGC
| Jvm运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收 |
-XX:+UseParNewGC | 打开此开关后,使用ParNew + Serial Old的收集器进行垃圾回收 |
-XX:+UseConcMarkSweepGC | 使用ParNew + CMS + Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用。 |
-XX:+UseParallelGC | Jvm运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old的收集器组合进行回收 |
-XX:+UseParallelOldGC | 使用Parallel Scavenge + Parallel Old的收集器组合进行回收 |
-XX:SurvivorRatio | 新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Subrvivor = 8:1 |
-XX:PretenureSizeThreshold | 直接晋升到老年代对象的大小,设置这个参数后,大于这个参数的对象将直接在老年代分配 |
-XX:MaxTenuringThreshold | 晋升到老年代的对象年龄,每次Minor GC之后,年龄就加1,当超过这个参数的值时进入老年代 |
-XX:UseAdaptiveSizePolicy | 动态调整java堆中各个区域的大小以及进入老年代的年龄 |
-XX:+HandlePromotionFailure | 是否允许新生代收集担保,进行一次minor gc后, 另一块Survivor空间不足时,将直接会在老年代中保留 |
-XX:ParallelGCThreads | 设置并行GC进行内存回收的线程数 |
-XX:GCTimeRatio | GC时间占总时间的比列,默认值为99,即允许1%的GC时间,仅在使用Parallel Scavenge 收集器时有效 |
-XX:MaxGCPauseMillis | 设置GC的最大停顿时间,在Parallel Scavenge 收集器下有效 |
-XX:CMSInitiatingOccupancyFraction | 设置CMS收集器在老年代空间被使用多少后出发垃圾收集,默认值为68%,仅在CMS收集器时有效,-XX:CMSInitiatingOccupancyFraction=70 |
-XX:+UseCMSCompactAtFullCollection | 由于CMS收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效 |
-XX:+CMSFullGCBeforeCompaction | 设置CMS收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用 |
-XX:+UseFastAccessorMethods | 原始类型优化 |
-XX:+DisableExplicitGC | 是否关闭手动System.gc |
-XX:+CMSParallelRemarkEnabled | 降低标记停顿 |
-XX:LargePageSizeInBytes | 内存页的大小不可设置过大,会影响Perm的大小,-XX:LargePageSizeInBytes=128m |
-XX:SurvivorRatio=1
内存分配与回复策略
- java提倡的内存管理归结为2个问题:内存分配与内存回收。
- 对象内存分配,一般就是在堆上分配内存(JIT编译后可能间接往栈上分配),对象主要分配在eden区上,(如果启用了线程分配缓冲,将按照优先在TLAB上分配,少数情况直接分配在老年代中)
TLAB的解释
堆内的对象数据是各个线程所共享的,所以当在堆内创建新的对象时,就需要进行锁操作。锁操作是比较耗时,因此JVM为每个线在堆上分配了一块“自留地”——TLAB(全称是Thread Local Allocation Buffer),位于堆内存的新生代,也就是Eden区。每个线程在创建新的对象时,会首先尝试在自己的TLAB里进行分配,如果成功就返回,失败了再到共享的Eden区里去申请空间。在线程自己的TLAB区域创建对象失败一般有两个原因:一是对象太大,二是自己的TLAB区剩余空间不够。通常默认的TLAB区域大小是Eden区域的1%,当然也可以手工进行调整,对应的JVM参数是-XX:TLABWasteTargetPercent。
对象优先在Eden分配
public class _11_TestAllocation {
private static final int _1MB = 1024 * 1024;
/**
* 初始堆大小 -Xms20M
* 最大堆大小 -Xmx20M
* 新生代大小 -Xmn10M
* -XX:SurvivorRatio=8 eden servival之比
* -XX:+PrintGCDetails 打印gc日志
*
* 所以
* old 10M
* Young 10m (eded 8 survival 1 survival 1)
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
*/
/*
[GC [DefNew:
7514K->572K(9216K), 0.0052474 secs]
7514K->6716K(19456K), 0.0052834 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Heap
def new generation total 9216K, used 4998K [0x32690000, 0x33090000, 0x33090000)
eden space
8192K,
54% used [0x32690000, 0x32ae26e0, 0x32e90000)
from space
1024K,
55% used [0x32f90000, 0x3301f190, 0x33090000)
to space 1024K, 0% used [0x32e90000, 0x32e90000, 0x32f90000)
tenured generation total 10240K, used 6144K [0x33090000, 0x33a90000, 0x33a90000)
the space 10240K, 60% used [0x33090000, 0x33690030, 0x33690200, 0x33a90000)
compacting perm gen total 12288K, used 209K [0x33a90000, 0x34690000, 0x37a90000)
the space 12288K, 1% used [0x33a90000, 0x33ac4530, 0x33ac4600, 0x34690000)
ro space 10240K, 45% used [0x37a90000, 0x37f17988, 0x37f17a00, 0x38490000)
rw space 12288K, 54% used [0x38490000, 0x38b1b5d8, 0x38b1b600, 0x39090000)
*/
public static void testAllocation() throws InterruptedException {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC
}
public static void main(String[] args) throws InterruptedException {
testAllocation();
}
}
|
大对象直接进入老年代
|
/**
* 大对象直接放old区中
*
* 可以通过 -XX:PretenureSizeThreshold=1048567 (1M) 直接指定,超过多少的大对象直接放old区
* Created by liuquan on 2016/12/9 17:27
*
*
* 初始堆大小 -Xms40M
* 最大堆大小 -Xmx40M
* 新生代大小 -Xmn20M
* -XX:SurvivorRatio=8 eden servival之比
* -XX:+PrintGCDetails 打印gc日志
*-XX:PretenureSizeThreshold 超过这个大小直接在老年代中分配
* 所以
* old 20M
* Young 20m (eded 16 survival 2 survival 12)
* VM参数:-verbose:gc -Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8
-XX:PretenureSizeThreshold=1048567
*
*
* 超过1M放到old区 1024*1024=1048576
*-verbose:gc -Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=104857
* 放一个9m的文件,那么eden和survival都放不下
*/
public class BigObject_To_Old_Region {
private static final int _1M = (1024*1024+5555);
public static void main(String[] args) {
byte b[] = new byte[_1M];
}
}
-verbose:gc -Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8
-XX:PretenureSizeThreshold=104857
Heap
def new generation total 18432K, used 1990K [0x31290000, 0x32690000, 0x32690000)
eden space 16384K, 12% used [0x31290000, 0x31481ae0, 0x32290000)
from space 2048K, 0% used [0x32290000, 0x32290000, 0x32490000)
to space 2048K, 0% used [0x32490000, 0x32490000, 0x32690000)
tenured generation total 20480K, used 1029K [0x32690000, 0x33a90000, 0x33a90000)
the space 20480K,
5% used [0x32690000, 0x327915c0, 0x32791600, 0x33a90000)
compacting perm gen total 12288K, used 188K [0x33a90000, 0x34690000, 0x37a90000)
the space 12288K, 1% used [0x33a90000, 0x33abf2f0, 0x33abf400, 0x34690000)
ro space 10240K, 45% used [0x37a90000, 0x37f17988, 0x37f17a00, 0x38490000)
rw space 12288K, 54% used [0x38490000, 0x38b1b5d8, 0x38b1b600, 0x39090000)
-verbose:gc -Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8
Heap
def new generation total 18432K, used 3020K [0x31290000, 0x32690000, 0x32690000)
eden space 16384K, 18% used [0x31290000, 0x315830a0, 0x32290000)
from space 2048K, 0% used [0x32290000, 0x32290000, 0x32490000)
to space 2048K, 0% used [0x32490000, 0x32490000, 0x32690000)
tenured generation total 20480K, used 0K [0x32690000, 0x33a90000, 0x33a90000)
the space 20480K,
0% used [0x32690000, 0x32690000, 0x32690200, 0x33a90000)
compacting perm gen total 12288K, used 209K [0x33a90000, 0x34690000, 0x37a90000)
the space 12288K, 1% used [0x33a90000, 0x33ac4430, 0x33ac4600, 0x34690000)
ro space 10240K, 45% used [0x37a90000, 0x37f17988, 0x37f17a00, 0x38490000)
rw space 12288K, 54% used [0x38490000, 0x38b1b5d8, 0x38b1b600, 0x39090000)
|
长期存活的对象将进入老年代
|
/**
* MaxTenuringThreshold来设置对象年龄为多少候进入老年代。默认为15
* Created by liuquan on 2016/12/12 10:49
*/
public class _12testTenuringThreshold {
private static final int _10MB = 1024 * 1024*10;
/**
* VM参数:-verbose:gc -Xms200M -Xmx200M -Xmn100M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1
* -XX:+PrintTenuringDistribution
*
*
*
* -Xms200M 最小堆200M
* -Xmx200M 最大堆200M
* -Xmn100M 新生代100M
* -XX:+PrintGCDetails 打印gc日志
* -XX:SurvivorRatio=8 survival和eden之比 ( eden 为80M ,survival为10M)
* -XX:MaxTenuringThreshold=1 对象年龄为1的就候就去老年代了
*/
@SuppressWarnings("unused")
public static void testTenuringThreshold() throws InterruptedException {
// Thread.sleep(10000);
byte[] allocation1, allocation2, allocation3;
allocation1 = new byte[_10MB / 4]; // 什么时候进入老年代决定于XX:MaxTenuringThreshold设置
allocation2 = new byte[4 * _10MB];
allocation3 = new byte[4 * _10MB]; //这里新生代垃圾回收一次
allocation3 = null;
allocation3 = new byte[4 * _10MB];
//Thread.sleep(10000);
}
public static void main(String[] args) throws InterruptedException {
new _12testTenuringThreshold().testTenuringThreshold();
}
}
-verbose:gc -Xms200M -Xmx200M -Xmn100M -XX:+PrintGCDetails -XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=1
[GC [DefNew: 46796K->3133K(92160K), 0.0184738 secs] 46796K->44093K(194560K), 0.0185106 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
[GC [DefNew: 45731K->0K(92160K), 0.0021731 secs] 86691K->44043K(194560K), 0.0021917 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 92160K, used 42598K [0x27290000, 0x2d690000, 0x2d690000)
eden space 81920K, 52% used [0x27290000, 0x29c29a10, 0x2c290000)
from space 10240K, 0% used [0x2c290000, 0x2c290088, 0x2cc90000)
to space 10240K, 0% used [0x2cc90000, 0x2cc90000, 0x2d690000)
tenured generation total 102400K, used 44043K [0x2d690000, 0x33a90000, 0x33a90000)
the space 102400K, 43% used [0x2d690000, 0x30192dd8, 0x30192e00, 0x33a90000)
compacting perm gen total 12288K, used 209K [0x33a90000, 0x34690000, 0x37a90000)
the space 12288K, 1% used [0x33a90000, 0x33ac4540, 0x33ac4600, 0x34690000)
ro space 10240K, 45% used [0x37a90000, 0x37f17988, 0x37f17a00, 0x38490000)
rw space 12288K, 54% used [0x38490000, 0x38b1b5d8, 0x38b1b600, 0x39090000)
-verbose:gc -Xms200M -Xmx200M -Xmn100M -XX:+PrintGCDetails -XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=15
[GC [DefNew: 46796K->3133K(92160K), 0.0213891 secs] 46796K->44093K(194560K), 0.0214255 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
[GC [DefNew: 45731K->3083K(92160K), 0.0029106 secs] 86691K->44043K(194560K), 0.0029382 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 92160K, used 46532K [0x27290000, 0x2d690000, 0x2d690000)
eden space 81920K, 53% used [0x27290000, 0x29cfe4f8, 0x2c290000)
from space 10240K, 30% used [0x2c290000, 0x2c592e50, 0x2cc90000)
to space 10240K, 0% used [0x2cc90000, 0x2cc90000, 0x2d690000)
tenured generation total 102400K, used 40960K [0x2d690000, 0x33a90000, 0x33a90000)
the space 102400K, 40% used [0x2d690000, 0x2fe90010, 0x2fe90200, 0x33a90000)
compacting perm gen total 12288K, used 209K [0x33a90000, 0x34690000, 0x37a90000)
the space 12288K, 1% used [0x33a90000, 0x33ac4560, 0x33ac4600, 0x34690000)
ro space 10240K, 45% used [0x37a90000, 0x37f17988, 0x37f17a00, 0x38490000)
rw space 12288K, 54% used [0x38490000, 0x38b1b5d8, 0x38b1b600, 0x39090000)
|
动态对象年龄判断
|
/**
* 动态对象年龄判断
*
* 为了更好的适应不同程序的内存状况,虚拟机并不是一定等到-XX:MaxTenuringThreshold才进入老年代。
* 如果在Survivor 空间中相同年龄所有对象大小的总和大于Survivor空间一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
* Created by liuquan on 2016/12/12 11:27
*/
public class _13testTenuringThreshold2 {
private static final int _10MB = 1024 * 1024*10;
/**
* VM参数:-verbose:gc -Xms200M -Xmx200M -Xmn100M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15
*
* -verbose:gc
* -Xms200M
* -Xmx200M
* -Xmn100M
* -XX:+PrintGCDetails
* -XX:SurvivorRatio=8
* -XX:MaxTenuringThreshold=15
*
*
* ===========
* 老年代 100
* eden 80
* survival 10
* -XX:+PrintTenuringDistribution
*
* 在日志中,我们发现Survivor空间的使用率为0,也就是说allocation1 ,allocation2对象都进入到了老年代中,而没有等到15岁的年龄。
*/
@SuppressWarnings("unused")
public static void testTenuringThreshold2() throws Exception {
byte[] allocation1, allocation2, allocation3, allocation4;
//Thread.sleep(10000);
allocation1 = new byte[_10MB / 4]; // allocation1+allocation2大于于survivo空间一半
allocation2 = new byte[_10MB / 4];
allocation3 = new byte[4 * _10MB];
allocation4 = new byte[4 * _10MB];
allocation4 = null;
allocation4 = new byte[4 * _10MB];
}
public static void main(String[] args) throws Exception{
testTenuringThreshold2();
}
}
[GC [DefNew: 49356K->5692K(92160K), 0.0213928 secs] 49356K->46652K(194560K), 0.0214236 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
[GC [DefNew: 48291K->0K(92160K), 0.0042351 secs] 89251K->46603K(194560K), 0.0042612 secs] [Times: user=0.00 sys=0.02, real=0.01 secs]
Heap
def new generation total 92160K, used 42598K [0x27290000, 0x2d690000, 0x2d690000)
eden space 81920K, 52% used [0x27290000, 0x29c29a10, 0x2c290000)
from space 10240K, 0% used [0x2c290000, 0x2c290088, 0x2cc90000)
to space 10240K, 0% used [0x2cc90000, 0x2cc90000, 0x2d690000)
tenured generation total 102400K, used 46603K [0x2d690000, 0x33a90000, 0x33a90000)
the space 102400K, 45% used [0x2d690000, 0x30412d70, 0x30412e00, 0x33a90000)
compacting perm gen total 12288K, used 209K [0x33a90000, 0x34690000, 0x37a90000)
the space 12288K, 1% used [0x33a90000, 0x33ac4558, 0x33ac4600, 0x34690000)
ro space 10240K, 45% used [0x37a90000, 0x37f17988, 0x37f17a00, 0x38490000)
rw space 12288K, 54% used [0x38490000, 0x38b1b5d8, 0x38b1b600, 0x39090000)
|
空间分配担保(做用,避免多次full GC)
|