`
crukor
  • 浏览: 31064 次
  • 性别: Icon_minigender_1
  • 来自: 青岛
社区版块
存档分类
最新评论

jvm对象分配及GC代码实例

阅读更多

虚拟机采用的是HotSpot内核

 

对象分配规则

  1.对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。

  2.大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝。通过参数-XX:PretenureSizeThreshold=3145728控制。

  3.长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,对象每熬过了1次Minor GC对象的年龄加1,达到阀值对象进入老年区。通过参数-XX:MaxTenuringThreshold=15(默认)控制

  4.动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无需等到MaxTenuringThreshold要求的年龄数

  5.空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。 (-XX:-HandlePromotionFailure)

 

术语说明:

Young Generation(新生代):分为:Eden区和Survivor区,Survivor区有分为大小相等的From Space和To Space。

Old Generation(老年代): 当 OLD 区空间不够时, JVM 会在 OLD 区进行 major collection。

 

Minor GC:新生代GC,指发生在新生代的垃圾收集动作,因为java对象大多都具备朝生夕死的特性,所以Minor GC非常频繁,一般回收速度也比较快。

    Full GC/Major GC:发生老年代的GC,对整个堆进行GC。出现Major GC,经常会伴随至少一次Minor GC(非绝对)。MajorGC的速度一般比minor GC慢10倍以上。

 

 

 

规则一:对象优先在Eden分配

 

  设置虚拟机参数为:-verbose:gc -Xms20M -Xmx20M -Xmn10M 

-XX:SurvivorRatio=8  -XX:+PrintGCDetails

 

  虚拟机提供了-XX:+PrintGCDetails 参数打印收集器日志,并且在进程退出时输出当前内存各区域的分配情况。

  通过 -Xms20M -Xmx20M -Xmn10M这3个参数限制java堆大小为20MB,且不可扩展。其中10MB分配给新生代,剩下的10MB分配给老年代。

  -XX:SurvivorRatio=8 决定了新生代中Eden区与一个Survivor区的比例为8:1,即Eden区=8MB,一个Survivor=1MB,另一个Survivor也为1MB。

 

/**
 * eden 对象通过分配担保机制提前转移到老年代去
 * vm 参数  -verbose:gc -Xms20M -Xmx20M -Xmn10M 
-XX:SurvivorRatio=8   -XX:+PrintGCDetails
*/
public class MinorGC {
	private static final int _1MB=1024*1024;
	public static void testMinorGC(){
	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) {
	testMinorGC();
}
}

 

控制台输出信息:

[GC [DefNew: 6471K->140K(9216K), 0.0074976 secs] 6471K->6284K(19456K), 0.0075433 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

Heap

 def new generation   total 9216K, used 4400K [0x029f0000, 0x033f0000, 0x033f0000)

  eden space 8192K,  52% used [0x029f0000, 0x02e18fd8, 0x031f0000)

  from space 1024K,  13% used [0x032f0000, 0x03313208, 0x033f0000)

  to   space 1024K,   0% used [0x031f0000, 0x031f0000, 0x032f0000)

 tenured generation   total 10240K, used 6144K [0x033f0000, 0x03df0000, 0x03df0000)

   the space 10240K,  60% used [0x033f0000, 0x039f0030, 0x039f0200, 0x03df0000)

 compacting perm gen  total 12288K, used 2105K [0x03df0000, 0x049f0000, 0x07df0000)

   the space 12288K,  17% used [0x03df0000, 0x03ffe6e0, 0x03ffe800, 0x049f0000)

No shared spaces configured.

 

  其中“[GC [DefNew:”后面内容为GC收集情况,DefNew为新生代GC

  Heap 后面内容为进程退出时输出当前内存各区域的分配情况。

eden space         eden

from space         Survivor

tenured generation   为年老代

compacting perm gen为永久代(方法区)

 

日志分析:在执行方法testMinorGC()分配allocation4 对象语句时,会发生Minor GC ,这次GC结果是新生代由6471K变为140KDefNew: 6471K->140K),而总内存占用量几乎没有减少(allocation123三个对象都是存活的,虚拟机没有找到可回收的对象)。

  这次GC发生的原因是给allocation4分配内存的时候,发现eden已经被占用了6MB,剩余的空间不足以分配allocation4所需的4MB内存,因此发生Minor GCGC期间虚拟机又发现已有的32MB大小的对象全部无法放入Survivor空间(Survivor空间只有1MB大小),所以只好通过分配担保机制提前转移到老年代去。

  这次GC结束后,4MBallocation4对象被顺利分配到eden中。因此程序执行完的结果是eden占用4MBSurvivor空闲,老年代被占用6MB

 

规则五:空间分配担保

  在发生Minor GC时,虚拟机会检测之前每次晋升到老年代的平均大小是否是否大于老年代剩余空间的大小,如果大于,则改为直接进行一次Full GC。如果小于,则查看HandlePromotionFailure设置是否允许担保失败,如果允许,则只进行Minor GC,如果不允许,则进行一次Full GC。

  老年代进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会活下来,在实际完成内存回收之前是无法明确知道的,所以只好取之前每一次回收晋升到老年达对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行Full GC来让老年代腾出更多的空间。

  如某次Minor GC存活后的对象突增,远高于平均值的话,会导致担保失败。如果出现了HandlePromotionFailure,那就只好在失败后重新发起一次Full GC。

 

/**
 * VM参数:-Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 
-XX:-HandlePromotionFailure  -XX:+PrintGCDetails 
*/

public class HandlePromotiontest {
  private static final int _1MB = 1024 * 1024;
  @SuppressWarnings("unused")
  public static void testHandlePromotion() {
	byte[] allocation1, allocation2, allocation3, allocation4, allocation5, allocation6, allocation7;
	allocation1 = new byte[2 * _1MB];
	allocation2 = new byte[2 * _1MB];
	allocation3 = new byte[2 * _1MB];
	allocation1 = null;
	allocation4 = new byte[2 * _1MB];//Minor GC
	allocation5 = new byte[2 * _1MB];
	allocation6 = new byte[2 * _1MB];
	allocation4 = null;
	allocation5 = null;
	allocation6 = null;
	allocation7 = new byte[2 * _1MB];//GC
  }

  public static void main(String[] args) {
	testHandlePromotion();
  }

}

 

设置HandlePromotionFailure

[GC [DefNew: 6471K->140K(9216K), 0.0043721 secs] 6471K->4236K(19456K), 0.0044109 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

[GC [DefNew: 6370K->6370K(9216K), 0.0000361 secs][Tenured: 4096K->4236K(10240K), 0.0045948 secs] 10466K->4236K(19456K), [Perm : 2086K->2086K(12288K)], 0.0047019 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

 

未设置HandlePromotionFailure

[GC [DefNew: 6471K->140K(9216K), 0.0042261 secs] 6471K->4236K(19456K), 0.0042664 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

[GC [DefNew: 6370K->140K(9216K), 0.0005921 secs] 10466K->4236K(19456K), 0.0006289 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

 

 

日志分析

设置HandlePromotionFailure参数时:

  在执行方法testHandlePromotion()allocation4 分配对象时,会发生第一次 GC(一般第一次都为Minor GC ,这次GC发生的原因是给allocation4分配内存的时候,发现eden已经被占用了6MB,剩余的空间不足以分配allocation4所需的2MB内存,因此发生Minor GC。而且已有的32MB大小的对象全部无法放入Survivor空间(Survivor空间只有1MB大小),所以只好通过分配担保机制提前转移到老年代去。

  第二次GC发生在给allocation7分配2MB对象时,此时eden已经被占用了6MB内存(allocation456),剩余内存不足需要转移到老年代。因为设置的是不允许担保失败,此时要进行一次Full GC

注意:第二次GC开始时[GC DefNew: 6370K->6370K,进行minor GC并没有立即执行GC,先判断是否可以空间担保分配,不允许则执行Full GC

 

未设置HandlePromotionFailure参数时:

  第一次GC同上。

  第二次GC发生在给allocation7分配2MB对象时,此时eden已经被占用了6MB内存(allocation456),剩余内存不足需要转移到老年代。虚拟机检测之前晋升到老年代的平均值(第一分配担保的值4MB即平均值)小于老年代剩余空间的大小因为设置的是允许担保失败,只需进行一次minor GC

 

分享到:
评论
1 楼 javacainiaosc 2017-12-17  
你好,MinorGC 这个示例,我运行的结果怎么跟你不一致呢

我运行结果是这样的:
Heap
PSYoungGen      total 9216K, used 7143K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 87% used [0x00000000ff600000,0x00000000ffcf9ff8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen       total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
PSPermGen       total 21504K, used 2720K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
  object space 21504K, 12% used [0x00000000f9a00000,0x00000000f9ca82a8,0x00000000faf00000)

相关推荐

    深入JVM内核 - 原理、诊断与优化

    栈分配及实例讲解 server与client模式 调试跟踪参数 介绍常用的JVM参数,包括内存分配、堆栈分配、虚拟机运行模式以及调试跟踪参数。 第四课 GC的算法和种类 引用计数 标记清除 复制算法 标记压缩 可触及性 本章是...

    java实现内存动态分配

    每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一对应一个堆,每一个线程有一个自己私有的栈。进程所创建的所有类的实例(也...

    JVM 知识点整理:GC垃圾收集器及相关算法

    JVM 知识点整理:GC垃圾收集器判断哪些对象需要回收引用计数器算法可达性分析算法引用还有分类(了解)“缓刑” finalize(了解)开始垃圾收集标记 – 清除算法复制算法标记 – 整理算法分代收集算法HotSpot 算法...

    Java学习笔记

    对象是类的实例化,类是对象的抽象 化。 引用 4.动态更新 5.简单! 没有指针和相关的内存操作 new 内存操作 gc 内存回收清理 Java 中的回收机制: 零引用回收 问题: System.gc(); Runtime.getRuntime().gc(); 调用...

    zabbix监控JAVA 进程 JVM数据统计分析告警监控

    zabbix自动发现JAVA进程,并进行自动添加监控JAVA 进程的JVM数据,统计分析告警监控。 支持多种中间件,tomcat、weblogic、微服务方式部署JAVA应用监控。 自带发现脚本和监控脚本,以及zabbix导入模板。

    关于JVM内存回收算法的补充

    对于Eden,大多数对象都是先在此区域开辟空间,存储年轻对象的实例数据。Eden相对于Old带,空间是比较小的。所以对象数据不断地在此开辟空间,Eden不够了,年轻代发生了回收。放置(稍后会说怎么放置)到S1或者S2。...

    JVM内存模型fager20200614.docx

    在 HotSpot 虚拟机中,分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)对象头(Header):包含两部分,第一部分用于存储对象自身的运行时数据,如哈希码、GC 分代年龄、锁状态标志、线程...

    Java新生代老年代的划分及回收算法

    JVM规范中说到:”所有的对象实例以及数组都要在堆上分配”。 Java堆是垃圾回收器管理的主要区域,百分之九十九的垃圾回收发生在Java堆,另外百分之一发生在方法区,因此又称之为”GC堆”。根据JVM规范规定的内容,...

    全面理解JVM虚拟机.rar

    全面理解JVM虚拟机 内容简介: 1、JVM主要学些什么 2、CLASS文件规范 3、类加载 4、执行引擎 5、GC垃圾回收 6、GC情况分析实例

    java8源码-jvm-study:jvm-study

    堆:实例对象 栈:栈帧(对象的引用,方法) 类加载 执行方法 创建对象 堆空间分代划分 outOfMemory异常 https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html 可以看jvm参数查看网址 Direct ...

    谈谈你对垃圾回收机制的了解?.docx

    一, 垃圾回收机制 在系统运行过程中会产生一些无用的对象,这些对象一直占用内存,不清理这些无用的对象可能会导致内存...jvm会把虚拟机栈和本地方法栈中正在引用的对象、静态属性引用的对象和常量,作为 GC Roots。

    10_对象的实例化内存布局与访问定位.pptx

    对象的实例化内存布局与访问定位

    weblogic内存调优

    Young保存刚实例化的对象。当该区被填满时,GC会将对象移到Old区。Permanent区则负责保存反射对象,本文不讨论该区。JVM的Heap分配可以使用-X参数设定, -Xms 初始Heap大小 -Xmx java heap最大值 …… ……

    net学习笔记及其他代码应用

    答:Class可以被实例化,属于引用类型,是分配在内存的堆上的,Struct属于值类型,是分配在内存的栈上的. [Page] 26.根据委托(delegate)的知识,请完成以下用户控件中代码片段的填写: namespace test { public ...

    【05-面向对象(下)】

    •静态内部类的对象寄存在外部类里,非静态内部类的对象寄存在外部类实例里 使用内部类 •1.在外部类内部使用内部类-不要在外部类的静态成员中使用非静态内部类,因为静态成员不能访问非静态成 员。 ...

    Singleton(单例模式)

    在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样有几个好处: 1、某些类创建比较频繁,对于一些大型的对象,这可以节省一笔很大的系统开销。 2、省去了new操作符,降低了系统内存的使用频率...

    新一代Java开发工具 可视化编辑 测试运维自动化.rar

    VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)...

    JProfiler入门教程

    1、监视堆内存占用情况和创建对象实例的数量找出内存泄露。 2、监视占用CPU较多的方法 3、监视线程的阻塞与死锁 4、监视GC的耗时。 监视本地JVM(Windows): 需要安装JProfiler windows版本 监视远程的JVM(Linux):...

    Java GC的那些事(1)

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

    direct-cache:缓存使用 jvm 堆外内存

    Direct-Cache 将缓存的值存储在不受 gc 管理的 jvm 直接内存(offheap)中,有了这个库,您可以缓存大量数据并且没有 gc 暂停问题。 Cache'key 仍在 jvm 堆中。 缓存值应该足够大(> 1k),所以直接缓存真的会有帮助...

Global site tag (gtag.js) - Google Analytics