JVM(二)—堆内存分析及对象的创建过程

JVM(二)—堆内存分析及对象的创建过程

1 堆内存

堆内存(Java Heap)是Java虚拟机管理的内存中最大的一块,被所有线程所共享。虚拟机启动时创建,存放对象实例和数组。 
Java堆中可以细分为:新生代和老年代。 
新生代:-Xmn

  • 由Eden区和Survivor区组成,比例是8:1,可通过-XX:SurvivorRatio = 8设置。
  • 新生对象一般在Eden分配内存。Eden不够时,产生第一次MinorGC,将存活对象放入S0内存区域,清空Eden;若仍然不够,对Eden 和 S0 区域 Minor GC,将Eden和SO存活的对象移动到S1区域,清空Eden和S0区。接着当Eden和S1再次满的时候又会进行Minor GC,此时会将Eden和S1区域存活对象放入S0区域,清空Eden 和 S1区域,如此循环。(对象在Survivor两个区域来回移动,每熬过一次Minor GC,GC年龄就会增1,超过阈值就会移动到老年代。)

老年代:Tenured Space ,Xmx 与 Xmn之差

  • 老年代一般存放新生代经过几次Minor GC依然存活的对象。 
    老年代空间不足时,会发生Full GC.

永久代:JDK1.8已经移除

2 堆上对象的创建过程

A a = new A(); 
(1)检查是否类加载

  • 当虚拟机遇到一条new指令时,首先看这个指令的参数A能否在常量池中定位一个类的符合引用,并检查这个类是否被加载,解析和初始化;

(2)分配内存 
接下来就是在堆中为新生对象分配内存,并设置初始值。 
分配算法有两种:

  • 指针碰撞:假如堆内存是规整的,空闲内存放一边,用过的内存放一边,中间放一个指针作为分界点的指示器。 
    当为新生对象分配内存的时候,只需要将指针向空闲内存移动与对象大小相等距离即可。
  • 空闲列表:如果堆内存不规整,那么虚拟机就要建个表记录哪些内存块可用。分配的时候从列表中找到一块够大的内存,分配给对象。

1)怎么选择分配算法? 
Java堆是否规整有垃圾收集器决定。 
使用Serial,PartNew等带有Compact压缩功能的收集器时,系统采用的分配算法是指针碰撞; 
使用CMS这种基于Mark-Sweep算法的收集器时,采用的是空闲列表。

2)分配时注意线程安全 
如果多线程为对象分配内存时,并发情况下非线程安全。 
解决方案: 
①使用CAS方法加失败重试的方法对分配内存的操作进行同步处理,保证操作的原子性。 
② TLAB:本地线程分配缓冲。即每个线程在堆中预先分配一块内存,这块内存叫做TLAB。哪个线程需要分配内存就在线程对应的TLAB上分配。 
    只有线程TLAB用完重新分配TLAB时才需要同步锁定。

(3)虚拟机对对象设置 
设置对象头,存放对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的GC分代年龄等信息。 
(4)按程序进行初始化:

按程序句语句为变量赋值。

 

3 对象的内存分配规则

1)对象优先在Eden区分配内存; 
2)大对象会直接进入老年代。大对象,即占用大量连续内存空间的Java对象(最典型的是那种很长的字符串及数组)。 
3)长期存活的对象进入老年代。 
虚拟机给每个对象定义一个对象年龄计数器。当对象在Eden出生且经过第一次Minor GC后依然存活且被Survivor容纳,每经历一次Minor GC,对象年龄就会+1。超过一个年龄阈值,就会晋升到老年代。-XX:MaxTenuringThreshold设置此年龄。 
4)如果Survivor空间中相同年龄所有对象大小大于Survivor空间的一半,就会移动到老年代。 
5)空间分配担保: 
在新生代发生MinorGC之前,虚拟机需要检查老年代最大可用的连续空间是否大于新生代所有对象总空间。如果成立,则MinorGC安全;如果不成立,则虚拟机查看HandlerPromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Monor GC,虽然有风险;如果小于,或者HandlerPromotionFailure设置不允许冒险,则改为Full GC.

4 参数及常见异常

1)参数 
Xms:堆最小值 
Xmx:堆最大值 
Xmn:新生代内存 
Xss:栈容量 
XX:PermSzie:方法区大小 
XX:MaxPermSize:最大方法区容量

2)异常 
OutOfMemoryError:内存溢出

  • 堆内存溢出:OutOfMemoryError:java heap space 
    原因:大量对象生成
  • 栈内存溢出:StackOverflowError 
    原因:线程请求的栈深度大于虚拟机所允许的最大深度
  • 方法区溢出:OutOfMemoryError:PermGen space 
    原因:大量产生Class对象类
  • 常量池溢出:OutOfMemoryError:PermGen space 
    原因:常量池产生大量字符串

你可能感兴趣的:(JVM)