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


相关文章
|
18天前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
29 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
68 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
56 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
1月前
|
算法 Java
JVM进阶调优系列(3)堆内存的对象什么时候被回收?
堆对象的生命周期是咋样的?什么时候被回收,回收前又如何流转?具体又是被如何回收?今天重点讲对象GC,看完这篇就全都明白了。
|
3月前
|
存储 Java 程序员
Java中对象几种类型的内存分配(JVM对象储存机制)
Java中对象几种类型的内存分配(JVM对象储存机制)
86 5
Java中对象几种类型的内存分配(JVM对象储存机制)
|
4月前
|
存储 监控 算法
(六)JVM成神路之GC基础篇:对象存活判定算法、GC算法、STW、GC种类详解
经过前面五个章节的分析后,对于JVM的大部分子系统都已阐述完毕,在本文中则开始对JVM的GC子系统进行全面阐述,GC机制也是JVM的重中之重,调优、监控、面试都逃不开的JVM话题。
135 8
|
4月前
|
存储 缓存 算法
(五)JVM成神路之对象内存布局、分配过程、从生至死历程、强弱软虚引用全面剖析
在上篇文章中曾详细谈到了JVM的内存区域,其中也曾提及了:Java程序运行过程中,绝大部分创建的对象都会被分配在堆空间内。而本篇文章则会站在对象实例的角度,阐述一个Java对象从生到死的历程、Java对象在内存中的布局以及对象引用类型。
130 8
|
1月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
37 4
|
13天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
11天前
|
Java Linux Windows
JVM内存
首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制。
10 1
下一篇
无影云桌面