一、对象的创建。
1、类加载检查。
当虚拟机执行到一条new指令的时候,首先会去检查指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化,如果没有就先进行加载。对应到代码中的操作是new、对象克隆、对象序列化等操作。
2、分配内存。
类加载完成后,对象所需的内存大小便可确定,此时需要找一块确定大小的空间去分配给新创建的对象,空间一般是在堆中,也有可能在虚拟机栈(方法内的局部变量如果不是逃逸对象则有可能被分配在虚拟机栈中,在出栈的时候被销毁)。
在分配内存的时候会遇到两个问题,分配的规则是什么以及并发情况下地址冲突如何处理。为了解决这些问题,jvm制定了指针碰撞和空闲列表两种分配方式。
- 指针碰撞 Bump the Pointer(默认使用指针碰撞方式分配)
如果堆中的内存是规整的,已经使用的内存区域和没有使用的内存区域有明确的界限,在交界处会有一个内存地址,这样新分配的对象就在交界处的内存地址加上自己需要的内存大小作为偏移量去分配。
- 空闲列表 Free List
如果堆中的内存空间是不规整的,已经使用的内存区域和没有使用的内存区域无规则的交错,那么这个时候jvm就得有个列表去存储可用的内存地址,这样在给新对象分配的时候去列表里面找到一块足够存放新对象的空间分配给新对象实例。
这样只是解决了空间分配规则问题,如果是并发场景的话还需要去通过CAS和TLAB去解决分配地址冲突的问题。
- CAS (compare and swap)
jvm采用CAS和失败重试机制保证对分配空间的同步处理。
- 本地线程分配缓冲(Thread Local Allocation Buffer)
给每个线程在堆中预先分配一块内存。
通过XX:+/ UseTLAB参数来设定虚拟机是否使用TLAB(JVM会默认开启XX:+UseTLAB),XX:TLABSize 指定TLAB大小。