10.21
Spring解决循环依赖的三级缓存:
10.28
- 对象的创建过程
1) 检查类是否已经被加载
2) 为对象分配内存空间
3) 为分配的内存空间初始化零值
4) 对对象进行其他设置(设置对象头)
5) 执行构造方法
流程:
1.当虚拟机执行到new 关键字时,首先会去运行时常量池中查找该引用所指向的类有没有被虚拟机加载,如果没有被加载,那么会进行类的加载过程,如果已经被加载,那么进行下一步。
2.当类元信息被加载之后,我们就可以从常量池找到对应的类元信息,通过类元信息来确定类型和后面需要申请的内存大小。
3.对象的内存分配完成后,还需要将对象的内存空间都初始化为零值,这样能保证对象即使没有赋初值,也可以直接使用。(分配完内存后,需要对对象的字段进行零值初始化,对象头除外,零值初始化意思就是对对象的字段赋0值,或者null值,这也就解释了为什么这些字段在不需要进程初始化时候就能直接使用)
4.分配完内存空间,初始化零值之后,虚拟机还需要对对象进行其他必要的设置,设置的地方都在对象头中,包括这个对象所属的类,类的元数据信息,对象的hashcode,GC分代年龄等信息
5.然后执行对象内部生成的init方法,初始化成员变量值,同时执行搜集到的{}代码块逻辑,最后执行对象构造方法。执行对象的构造方法,这里做的操作才是程序员真正想做的操作,例如初始化其他对象啊等等操作,至此,对象创建成功。
- DCL(Double Check Lock)单例模式需不需要volatile?
需要,volatile在DCL单例中不是使用它的线程可见性,而是禁止指令重排序。
详情:https://blog.csdn.net/qq_2681... - 对象在内存中的存储布局
https://blog.csdn.net/ljc1026... - 对象头具体包括什么
markword:用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程池有的锁等,为4字节(32位)或8字节(64位)
类型指针:即对象指向它的类元数据(保存在方法区)的指针,虚拟机通过这个指针来确定这个对象属于哪个类的示例,为4字节(32位)或8字节(64位,但默认开启压缩,为4字节)
数组长度(只有数组对象才有):用4个字节int来记录数组长度 - 对象怎么定位
java中对象的访问定位有两种方式
我们熟知的是方式2,栈中的引用直接指向堆中的实例对象的内存地址
那么为什么还会存在方式1 使用句柄呢?
答:好处就是,不管堆中的实例对象地址是否改变,是否被垃圾回收,栈中的引用是不会发生改变的,改变的是堆中句柄池所保存的内存地址,则栈中的reference不需要被修改。
使用方式2 直接指针有什么好处呢?
答:引用直接指向堆内存中的对象地址,相比使用句柄,减少了一次寻址过程,减少了一次性能开销,由于对象的访问定位频率非常高,节约了性能,速度快。
那么HotspotVM使用的是哪种对象访问定位方式呢?
答:HotspotVM使用的是方式2 直接指针的定位方式
- 对象怎么分配
https://juejin.cn/post/684490... - Object o = new Object()占用多少字节
markword:8
类型指针:4
padding对齐:4
总共16字节 - 为什么hotspot不用c++对象表示java对象
c++中,动态绑定通过虚函数来实现,代价是每个c++对象都必须维护一张虚函数表。Java的特点是一切皆是对象,如果每个对象都维护一张虚函数表,内存开销将会非常大。JVM对此做了优化,虚函数表不再由每个对象维护,改成由class类型维护,所有属于该类型的对象共用一张虚函数表。
HotSpot采用Oop-Klass模型来表示Java对象,其中Klass对应着Java对象的类型(Class),而Oop则对应着Java对象的实例(Instance)。Oop是一个继承体系,其中oop是体系中的最高父类,它的存储结构可以分成对象头和对象体。对象头存储的是对象的一些元数据,对象体存储的是具体的成员属性。值得注意的是,如果成员属性属于普通对象类型,则oop只存储它的地址。 - class对象是在堆还是在方法区
Class对象是存放在堆区的,不是方法区,这点很多人容易犯错。类的元数据(元数据并不是类的Class对象!Class对象是加载的最终产品,类的方法代码,变量名,方法名,访问权限,返回值等等都是在方法区的)才是存在方法区的。
11.4
g1:部分解决gms的内存碎片问题,可预测的停顿时间
11.12
11.15
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。