jvm对象创建和分配机制

对象的创建

类加载检查

当虚拟机执行new Object()时,会去常量池检查是否能定位到该类的符号引用,并检查这个符号引用所代表的类是否已经加载过,如果未经加载过,则通过相应的类加载器进行加载。

分配内存

在类加载检查通过后,虚拟机已经确定该类在内存中需要分配的空间大小,于是会去内存中划分一块可以存放该类的内存空间来存储这个对象:

指针碰撞

jvm堆内存是完全规整的情况下,维护一个指针将内存划分为两块区域,分别是已使用的内存区域和未使用的内存区域,分配空间直接通过指针来划分。

空闲链表

jvm堆内存区域在不规整的情况下,通过维护一个空闲链表来记录空闲内存的大小和地址,当需要分配内存时,查询空闲链表找到对应的内存区域即可。

并发:CAS;本地线程分配缓冲(即每个线程在Java堆中预先分配一小块内存。通过­XX:+/­UseTLAB参数来设定虚拟机是否使用TLAB,虚拟机默认开启

初始化

内存分配完成后,虚拟机将分配到的内存空间都初始化为零值,相当于将对象的成员属性赋零值

设置对象头

对象在内存中主要分为三部分:对象头,实例数据,对其填充。

对象头又分为三个部分:运行时数据,包含对象的hashCode、分代年龄、偏向锁的线程id、锁标志位以及指向栈中记录的指针;

常量池中类元信息的指针(Klass pointer):如果开启指针压缩(‐XX:+UseCompressedOopsm默认开启)占用4个字节大小空间,关闭指针压缩则占用8个字节。

数组长度(如果不是数组对象则不存在):占用4个字节

执行<init>方法

执行<init>方法,即给对象赋予程序员设置的值以及执行构造方法。

对象内存分配

对象在栈上分配

在对象分配内存时,虚拟机会做一些额外的优化处理,比如对象可能会被分配在栈中,虚拟机通过逃逸分析判断在方法内部创建的对象如果只在该方法内部使用,那么这个对象会被分配在栈中。另外如果开启标量替换,将不创建该对象直接使用该方法的成员变量来代替对象的属性值,将他们分配在栈帧或者寄存器中。

逃逸分析使用参数-XX:+DoEscapeAnalysis设置,默认开启

标量替换使用参数-XX:+EliminateAllocations设置,默认开启

对象在Eden区分配

大部分情况下,新创建的对象都会被分配到新生代的Eden区域。当新生代的Eden内存区域满了以后,jvm会触发minor GC。做个测试,其中byte的值根据自己的电脑内存情况进行调整。

//添加运行JVM参数: -XX:+PrintGCDetails
public class GCTest {
   public static void main(String[] args) throws InterruptedException {
      byte[] b = new byte[27280*1024];
   }
}

 执行结果如下,对象b已经把Eden区内存占满了

继续测试代码

    public static void main(String[] args) throws InterruptedException {
      byte[] b, b2;
      b = new byte[27280*1024];
      b2 = new byte[8000*1024];
   }

结果发现触发了一次minor GC,因为在第一次分配内存对象b时,将Eden区内存占满了,在分配b2对象时,Eden区没有足够的内存区域导致触发minor GC。GC之后将对象b移到老年代内存区域,对象b2会被分配到Eden区域。

大对象直接进入老年代

jvm通过参数-XX:PretenureSizeThreshold可以设置对象达到指定的大小时,会直接被分配到老年代。但是这个参数只会在Serial和ParNew两个垃圾收集器中生效,可以通过-XX:+UseSerialGC指定垃圾收集器。

目的是为了避免为大对象分配内存时的复制操作而降低效率。

长期存活的对象进入老年代

新生代中的对象在每经过一次minor GC时,对象头的分代年龄都会+1,当超过15(不过不同的垃圾收集器会有所不同)后,对象会晋升到老年代中。分代年龄也可以通过参数-XX:MaxTenuringThreshold来设置。

对象动态年龄判断

新生代中survivor区域某一批对象的总和大小超过survivor的50%后,大于等于这批对象中的最大年龄的对象会直接进入老年代了。该机制会在minor GC之后触发。

老年代空间分配担保机制

为了确保老年代有足够的空间容纳minor GC后存活的对象,这就是jvm的老年代空间分配担保机制。在每次minor GC之前,jvm会判断即将minor GC的年轻代对象是否小于老年代剩余的空间,是则进行minor GC,否则判断jvm是否设置​​​​​​​了-XX:-HandlePromotionFailure的参数(1.8默认设置),如果没有设置直接full GC然后minor GC,如果设置了则判断历史每一次minor GC存活对象的平均所用空间是否小于老年代剩余空间,是则minor GC,否则full GC然后minor GC。

对象内存回收

引用计数法

每个对象会记录自己被引用的次数,如果为0,表示该对象为垃圾对象,但是存在循环引用导致垃圾对象不能被回收

可达性分析算法

通过GCRoots(线程栈的本地变量、静态变量、本地方法栈的变量) 向下搜索所有引用的对象,找到的对象被标记为非垃圾对象,然后将其他没有被引用的对象回收。

方法区如何判断一个类是无用的类

该类的所有实例都被垃圾回收器回收

该类的加载器已经被回收了

该类的java.lang.Class对象没有被任何地方引用 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值