JVM - 写了这么多年代码,你还不知道new对象背后的逻辑?

简介: JVM - 写了这么多年代码,你还不知道new对象背后的逻辑?

202006222317017.png

对象创建流程

20200622234155722.png


我们知道JVM三大组成部分: 类加载子系统、运行时数据区 、字节码执行引擎。

要想new 一个对象,肯定是要绕不开JVM的机制。


类加载检查】


JVM启动的时候并不是将所有的类都初始化,所以当碰到一个new指令时,JVM首先会去检查这个类有没有被加载,具体就是去常量池中看是否有这个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析和初始化过 。 若没有这必须经历【类加载子系统】的历练 (加载–校验–准备–解析–初始化)


JVM-白话聊一聊JVM类加载和双亲委派机制源码解析


【分配内存


类加载校验通过后 ,是不是该分配内存了呢?


是的, 接下来JVM将会为这个新生的对象分给内存,因为这个新生对象所需要内存大小在类加载完之后便可以完全确定,对象放哪里呢? 通常都是放在堆中,所以所谓的分配内存实际上就是从Java堆中划分出一块固定大小的内存给这个新生对象。


虽然很简单的一件事情,但是要考虑的地方可不少


采取何种方式分配内存

并发问题


内存划分的两种方式


JVM提供了2中划分内存的方法


指针碰撞(Bump the Pointer) 【默认方式


如果堆中的内存是绝对规整的,大家都按顺序排放,分配过内存的对象那个在一边,未使用的内存在另外一边 ,分界线使用指针来维护。因为新生对象所需要内存大小在类加载完之后便可以完全确定,所以仅需要将指针移动对象大小的位置即可。


当然了这是一种理想的情况,JVM里还有GC,会标记清除等等


空闲列表(Free List)


如果堆内存中的内存并不是规整的,分配的内存和未分配的内存糅杂在一起, 如果还用上面的指针碰撞的方式, 如果移动的可用内存无法容纳这个对象,放不下啊? 咋弄? 继续碰么?


显然效率很低。 所以JVM采用了另外一种方式,JVM维护了一个列表,记录了堆中的可用内存,那么分配内存的时候就从JVM维护的列表中找一个足够容纳这个对象的内存区域给它,并更新列表记录。


解决分配内存并发问题的两种方式


第二个问题 并发问题如何解决呢?


在并发的情况下,可能出现JVM正在给对象A分配内存,但是指针还没来得及修改,对象B又使用了A的内存空间的情况。


为了解决这个问题,JVM采取了


CAS (compare and swap)

简而言之就是JVM采用【 CAS+失败重试 】保证更新操作的原子性 。


本地线程分配缓冲 (Thread Local Allocation Buffer , TLAB)

把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存


通过­XX:+/­ UseTLAB参数来设定虚拟机是否使用TLAB。


JDK8中默认开启XX:+UseTLAB ,默认值eden区域的1%,当然了也可以通过-XX:TLABSize 指定TLAB大小 。 一般不建议修改。


如果TLAB还放不下,那就走CAS了…


不管怎么分配,目的只是为了更好的回收内存或者更快的分配对象


【初始化】


内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头).

如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。

这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的默认值 (比如 int 默认0 , String 默认null , boolean 默认false等等)


【设置对象头】


初始化默认值以后,JVM要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头Object Header之中。


这部分数据的长度在32位和64位的虚拟机中分别为32个和64个bits,官方称它为“Mark Word”。


对象的组成


在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、 实例数据(Instance Data)和和对齐填充(Padding) 。


2020062514454547.png


对象头的两部分组成


HotSpot虚拟机的对象头包括两部分信息

  • 第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时 间戳等。


32位操作系统为例



20200625170635537.png

  • 对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

如下所示


20200625161547383.png


【执行init方法】


执行方法,即对象按照程序员的意愿进行初始化。对应到语言层面上讲,就是为属性赋值(注意,这与上面的赋零值不同,这是由程序员赋的值) 和执行构造方法。

IDEA安装jclasslib插件可以查看

202006261039515.png

20200626104851385.png


20200626104736432.png


这里的init实际上是C++调用的,相对于面向开发人员 就是 new Artisan() ,并执行Artisan默认的构造函数。



总结一下



20200625141715138.png


相关文章
|
22天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
45 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
22天前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
43 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
24天前
|
算法 Java
JVM进阶调优系列(3)堆内存的对象什么时候被回收?
堆对象的生命周期是咋样的?什么时候被回收,回收前又如何流转?具体又是被如何回收?今天重点讲对象GC,看完这篇就全都明白了。
|
4月前
|
存储 监控 算法
(六)JVM成神路之GC基础篇:对象存活判定算法、GC算法、STW、GC种类详解
经过前面五个章节的分析后,对于JVM的大部分子系统都已阐述完毕,在本文中则开始对JVM的GC子系统进行全面阐述,GC机制也是JVM的重中之重,调优、监控、面试都逃不开的JVM话题。
117 8
|
4月前
|
存储 缓存 算法
(五)JVM成神路之对象内存布局、分配过程、从生至死历程、强弱软虚引用全面剖析
在上篇文章中曾详细谈到了JVM的内存区域,其中也曾提及了:Java程序运行过程中,绝大部分创建的对象都会被分配在堆空间内。而本篇文章则会站在对象实例的角度,阐述一个Java对象从生到死的历程、Java对象在内存中的布局以及对象引用类型。
118 8
|
4月前
|
JSON Java BI
一次Java性能调优实践【代码+JVM 性能提升70%】
这是我第一次对系统进行调优,涉及代码和JVM层面的调优。如果你能看到最后的话,或许会对你日常的开发有帮助,可以避免像我一样,犯一些低级别的错误。本次调优的代码是埋点系统中的报表分析功能,小公司,开发结束后,没有Code Review环节,所以下面某些问题,也许在Code Review环节就可以避免。
158 0
一次Java性能调优实践【代码+JVM 性能提升70%】
|
5月前
|
算法 Java
Java垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一种自动内存管理机制,用于在运行时自动回收不再使用的对象所占的内存空间
【6月更文挑战第18天】Java的GC自动回收内存,包括标记清除(产生碎片)、复制(效率低)、标记整理(兼顾连续性与效率)和分代收集(区分新生代和老年代,用不同算法优化)等策略。现代JVM通常采用分代收集,以平衡性能和内存利用率。
70 3
|
5月前
|
存储 缓存 算法
JVM对象创建与内存分配机制
该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
41 0
|
22天前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
33 4
|
25天前
|
存储 缓存 算法
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!