《JVM对象创建与内存分配机制深度剖析》
一、创建对象

1、jvm得到new指令后,比如new User()对象, 此时就会携带User()对象到常量池中寻找是否有User的class信息,也就是符号引用,如果没有就重新去加载;
2. 如果已经加载完毕,就会在堆中给当前对象分配内存空间;
如何分配内存空间?
指针碰撞: 这种分配方式前提是内存对象都是有规则有秩序的排列,这时候,就会把当前已分配内存的最后一个对象后的指针移动即将创建对象大小的空间来存放此对象;
空间列表:这种分配方式前提是内存无规则分配,这种情况就必须提前记录哪些空间是没有分配的;
以上分配内存空间,都会面临一些问题,对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况,这种情况如何解决?
CAS加失败重试,说白了,就是比较加失败了重新尝试;
TLAB(Thread local allocation buffer 本地线程分配缓存), 每一个线程都会提前在内存中分配一块空间,来存放当前自己的对象;
3、初始化,内存分配完毕后,虚拟机会把分配的内存空间赋值为零值;
4、设置对象头(首先解释一下,一个对象总共包括:对象头,实例数据,对齐填充)
对象头(Object Header) 有包括, Mark Word、Klass Pointer、数组的长度;
Mark Word : 哈希值、GC分代年龄、锁状态标志、线程持有锁,偏向线程D、偏向时间戳;
Klass Pointer(类型指针): 对象指向类元数据的指针,实际类型指针会进行压缩,也就是所谓指针压缩;(这里的Klass 不是我们java中的Class,这个Klass是对象在方法区元数据中的类的指针,是jvm获取类信息用的,而咱们java中Class只是提供咱们开发人员的一个类来获取对象的类信息)
数组长度: 如果对象是数组或者包含数组, 对象头里会有数组的长度;

为什么要指针压缩? 传输对象时,防止带宽严重消耗,同时也是为了占用大量的内存空间 1.7JDK默认开启 也可以通过参数开启或者关闭
5、执行init方法
这里的init方法进行初始化就是按照开发人员的代码来进行赋值,也就是给属性进行赋值,这里和上面的初始化值为零不一样,是根据程序员赋需要的值;
二、对象在内存的分配

下面咱们各个节点攻破:
1、创建对象后,判断栈上分配,解释前先介绍两个概念, 逃逸分析以及标量替换
逃逸分析(1.7JDK之后默认开启逃逸分析,也可以用参数-XX:+DoEscapeAnalysis): 指当前对象创建后,被其他方法调用,这时候就要逃逸分析,看图,看注释,立马明白!

标量替换:就是当判断当前对象是非逃逸分析,那么jvm会把次对象剖析,把此对象的属性分散存入到栈空间中这就叫标量替换!
那么解释完逃逸分析和标量替换后,我们来说为什么会判断栈上分配呢?
对于有一些对象,如果非逃逸分析,是在本线程本栈帧中使用的,没有被其他地方调用(先不考虑大对象啊,如果是大对象,栈空间也存不下,还是会存到堆中),那么jvm就可以把这样的对象通过标量替换的方式存入到栈中(分散存属性,也是避免因为对象存是需要连续的空间,但是栈空间不是很大),同时也避免了大量的GC,提高性能,因为栈最大的特点就是,用完就出栈销毁;
2、大对象优先分配的老年代区
主要还是避免了新生代来回的进行minor GC 来回在eden区和survivor区大量的复制;
3. 通过TLAB(Thread local Allaction Buffer,本地线程分配缓存)分配空间后,就会进入新生代eden区,对象进入新生代后,会有eden区、survivor1(图中S1,内存中也叫to)区和 survivor2 (图中S2,内存也叫from)区;
年龄大的进入老年区: 当eden区内存空间占满后,回发生minor GC,然后把存活下来的对象放入到survivor区的空闲空间,比如当前to区是空闲的,这时候就回给存活下来的对象年龄计数器上+1,等eden区再次满后,又会进行minor GC,这时垃圾回收会把eden区和to区进行GC操作,然后存活下来的对象放入到form区,这是存活下来的对象年龄计数器在之前的基础上再次+1,这样操作一直重复,直到存活下来的对象年龄到默认的15,这时就把达到15的对象,放入到老年代区;(15可以设置:可以通过参数 -XX:MaxTenuringThreshold 来设置)
动态年龄判断:存活在survivor区的所有的对象年龄相加大于survivor区的50%,那么大于等于(相加年龄和)的就会进入老年代;
老年代空间分配担保:(发生在minor GC之前)老年代剩余空间与新生代的所有的对象(包括垃圾对象)进行比较,如果是大,就直接 minor GC,如果是小,那么此时就会看一下
三、对象内存回收
如何判断对象是否是垃圾对象呢?
1、引用计数法:对象只要被引用一次+1,不被引用-1; 这样的计算法有弊端,有时候遇到两个对象互相引用,但是这两个对象已经不用了,这时候这样的对象就无法回收,看代码注释如下:

(看注释应该明白了,代码全部走完后,发现A和B对象最终各自都还是1,因为互相引用了,无法垃圾回收,但是两个对象已经没用了);
2.可达性分析算法:GC Root 局部变量,线程栈上本地变量,静态变量,本地方法栈的变量
就是通过GC Root进行向下寻找引用的对象,反是有引用的就会形成引用链,在引用链上的对象叫非垃圾对象,不在引用链上的对象,叫垃圾对象;
类如何被回收?垃圾类需要满足以下几点?
1、当前类在内存中是否还对象实例;
2、 加载此类的ClassLoader不存在;
3、该类的java.lang.class对象无任何地方引用,无法通过反射访问改类的方法;
本人工作3年中级菜鸟程序员, 最近想回顾一下知识,做了一些简单总结同时也为了自己今后复习方便,如果有逻辑错误,大家体谅,同时也希望大牛们能给出正确答案让我改正,谢谢!