对象创建的主要流程
根据垃圾收集器的不一样,分配的内存方法略有不同
但是不管是那种分配方式,都会存在并发问题
解决并发问题的方法
相当于给静态变量等等一些列的方法赋予一个初值例如int i 默认初始化0 对象初始化null 等等
初始化之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息、对象的哈希码、对象的jc分代年龄等信息。讲这些信息放在对象的对象头Object Header之中
这里让我们来看看对象里面的具体数据
这里先导入依赖
org.openjdk.jol jol‐core 0.9
编写代码
package com.ruoyi.framework.com.qin;import org.openjdk.jol.info.ClassLayout;public class TestClassHeader {public static void main(String[] args) {ClassLayout classLayout = ClassLayout.parseInstance(new Object());System.out.println(classLayout.toPrintable());System.out.println();ClassLayout classIn = ClassLayout.parseInstance(new int[]{});System.out.println(classIn.toPrintable());System.out.println();ClassLayout classA = ClassLayout.parseInstance(new A());System.out.println(classA.toPrintable());System.out.println();}public static class A{int id;String name;Object o ;}}
下面是结果
对象对齐
设置完对象头,按照程序员的意愿来赋值
以前一般情况下我们是分配在堆里面的,后来慢慢地我们不一定了
这里有个比方 有两段代码
假如现在调用了test1()方法,返回了user,说明这个user是逃逸了,在其他地方是有用到的,下一个test2其实这个方法的user对象就只在当前方法下有用,说明没有逃逸,那么为什么我们不把他放在栈里面跟着方法执行完毕就把他释放掉,而要放在堆里面等待垃圾回收
jvm里面提供了一个方法,一般情况下默认开启了逃逸分析来优化内存,并且逃逸分析和标量替换会同时开启
刚刚讲的是放在栈上面的如果是放在对上面的呢?
这里有个一例子
假设Eden区的内存大小为65536k s0区10752k s1区10752k 老年代175104k 那么
那这个如果我们想要减少垃圾回收,由于老年代比年轻代大得多,大对象我们是不是可以放入老年代呢,大对象就是需要大量连续空间的对象,(比如字符串、数组等等),那么JVM这里提供了一个参数 -XX:PretenureSizeThreshold可以直接设置大对象直接进入老年代,不会进入年轻代,这个参数只在Serial和ParNew两个收集器下有效哦
这里有一个机制:对象动态年龄判断机制,如果一批对象大于这块Survicor区域内存大小的50%,会直接进入老年代。例如Survivor区域里出现了一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor的区域的50%,此时会把年龄n和n以上的对象都放入老年代。这个规则其实是希望那些可以长期存活的对象,尽早进入老年代
老年代空间担保机制
就是gc垃圾回收,是如何回收的,这里一般用的是可达性分析算法,还有一个引用计数器算法但是一般不用,当发生相互引用的情况下这个算法会出现问题,相当于内存泄漏
可达性分析算法里面有一个finalize()方法,这个方法继承Object
当垃圾收集器准备回收这个类的实例的时候,就会执行这个方法,我们可以在这里建立与其他对象的连接,例如把自己的赋值给某个类或者变量挥着对象的成员变量。那在第二次标记清除的时候会将他移除即将回收的集合。如果这个对象还是没能逃脱,那么基本上他就真的被回收了
注意:一个对象的finalize()方法只会被执行一次,也就是通过调用finalize方法自我救命的机会只有一次