1. Java对象的创建过程

类加载检查 ===> 分配内存 ===> 初始化零值 ===> 设置对象头 ===> 执行init方法

1.1 类加载检查

虚拟机遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,就必须执行相应的类加载过程。

1.2 分配内存

在类加载检查通过后,虚拟机将为新生对象分配内存。

对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。分配方式包括指针碰撞空闲列表选择哪种方式取决于Java堆是否规整,Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

  • 指针碰撞:适用于堆内存规整(没有内存碎片)的情形。原理:用过的内存整理到一边,未使用的内存放在另一边,中间有一个分界值指针,只需要向着未使用过的内存方向将该指针移动对象内存大小位置即可。采用的GC收集器有Serial和ParNew(因为使用标记-整理,不存在内存碎片)。
  • 空闲列表:适用于堆内存不规整的情形。原理:虚拟机维护一个列表,该列表中记录哪些内存块可用,在分配的时候,找一块足够大的内存块来划分给对象实例,最后更新列表记录。采用GC收集器为CMS(因为采用标记-清除算法,堆内存不规整)。

内存分配的并发问题,虚拟机采用两种方式来保证线程安全。

  • CAS+失败重试:CAS是乐观锁的一种实现方式。所谓乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。
  • TLAB:为每一个线程先在Eden区分配一块儿内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中剩余内存或TLAB内存用尽时,再采用CAS进行内存分配。

1.3 初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),保证对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

1.4 设置对象头

初始化零值之后需要对对象头进行必要的设置。

1.5 执行init方法

上面工作完成后,在虚拟机的视角,新的对象已经产生。从Java程序的视角,执行new指令后就会执行init方法,把对象按照程序员的意愿进行初始化,从而得到一个真正可用的对象。

2. 对象访问定位的方式

①使用句柄:在Java堆中创建句柄池,句柄包含对象实例数据和对象类型数据,本地变量表中的reference存储对象的句柄地址。

②直接指针:本地变量表中存储的直接就是对象的地址。

直接指针速度快,而使用句柄的最大好处是reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference本身不需要修改。

3. Java中Class文件字节码结构

4. 类加载过程和类的生命周期

类加载过程:加载、连接、初始化

类的生命周期:加载、连接、初始化、使用、卸载

  • 加载:通过全类名获取定义此类的字节流;将字节流所代表的静态存储结构转换为方法去的运行时数据结构;在堆内存中生成一个代表该类的Class对象,作为方法区这些数据的访问入口。
  • 连接:包括三步:验证、准备、初始化。①验证:验证文件格式、元数据、字节码符号引用;②准备:为类的静态变量分配内存,并将其初始化默认值;③解析:把类中的符号引用转换为直接引用。
  • 初始化:维蕾德静态变量赋予正确的初始值。
  • 使用:new出对象在程序中使用。
  • 卸载:执行垃圾回收。

5. 类加载器

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。

JVM中内置了三个重要的ClassLoader,除了BootstrapClassLoader,其它类加载器均由Java实现且全部继承自java.lang.ClassLoader。

  • BootstrapClassLoader(启动类加载器):最顶层的加载器,由C++实现,负责加载Java核心类库,即%JAVA_HOME%/lib目录下的jar包和类。
  • ExtensionClassLoader(扩展类加载器):负载加载Java扩展库,即%JRE_HOME%/lib/ext目录下的Jar包和类。
  • AppClassLoader(应用程序加载器):负责加载当前拥有classpath下的所有jar包和类。

6. 双亲委派

6.1 双亲委派机制

每一个类都有一个对于它的类加载器。系统中的ClassLoader在协同工作时会默认使用双亲委派机制。

在类加载的时候,首先判断该类是否被加载,已经加载过的类无需加载会直接返回,否则会自己尝试加载。加载的时候,首先会把该请求委派给父类加载器进行处理,因此所有的请求最终都会传送到顶层的启动类加载BootstrapClassLoader加载器中。当父类加载加载器无法处理时,才会自己进行处理。当父类加载器为null时,会使用BootstrapClassLoader作为父类加载器。

6.2 双亲委派机制的作用

  • 避免类的重复加载。即使是相同的类文件,被不同的类加载器加载后产生的也是不同的两个类。
  • 保证了核心API不被篡改。
  • 保证了Java程序的稳定运行。

6.3 打破双亲委派机制的方法

  • 自定义一个类加载器,重写loadClass()方法。
  • 引入线程上下文类加载器

6.4 打破双亲委派机制的场景

6.4.1 JDBC

JDBC:使用线程上下文类加载器(Thread Context ClassLoader)

JDBC的Driver接口定义在JDK中,其实现由数据库的服务商来提供,比如MySQL驱动包。DriverManager 类中要加载各个实现了Driver接口的类,然后进行管理,其由BootstrapClassLoader加载,而其Driver接口的实现类是位于服务商提供的 Jar 包,根据类加载机制,当被装载的类引用了另外一个类的时候,虚拟机就会使用装载第一个类的类装载器装载被引用的类。也就是说BootstrapClassLoader还要去加载jar包中的Driver接口的实现类。然而BootstrapClassLoader默认只负责加载 $JAVA_HOME中jre/lib/rt.jar 里所有的类,所以需要由子类加载器去加载Driver实现,这就破坏了双亲委派模型。

6.4.2 Tomcat

Tomcat:自定义类加载器,Tomcat的类加载器如下图。

每个Tomcat的WebappClassLoader加载自己目录下的class文件,不会传递给父类加载器,破坏了双亲委派机制。

Tomcat自定义了很多来加载器,可能处于以下目的:

  • 对于各个 webapp中的 class和 lib,需要相互隔离,不能出现一个应用中加载的类库会影响另一个应用的情况,而对于许多应用,需要有共享的lib以便不浪费资源;
  • 与 JVM一样的安全性问题。使用单独的 ClassLoader去装载 Tomcat自身的类库,以免其他恶意或无意的破坏;
  • 热部署。Tomcat修改文件不用重启就会自动重新装载类库。

jvm类加载过程_详解JVM类加载相关推荐

  1. java 标量替换_详解jvm中的标量替换

    概述 通常在java中创建一个对象,大家都认为是在堆中创建. 在jdk6开始有逃逸分析,标量替换等技术,关于在堆中创建对象不再绝对. 关于标量替换,通过以下几点进行概述: 逃逸分析 标量替换是什么 测 ...

  2. java加载机制_详解Java类加载机制

    一:ClassLoader 从JVM结构图中可以看到,类加载器的作用是将Java类文件加载到Java虚拟机. HotSpot JVM结构,图片来自Java Garbage Collection Bas ...

  3. java双缓存机制_详解JVM类加载机制及类缓存问题的处理方法

    前言 大家应该都知道,当一个Java项目启动的时候,JVM会找到main方法,根据对象之间的调用来对class文件和所引用的jar包中的class文件进行加载(其步骤分为加载.验证.准备.解析.初始化 ...

  4. java 内存跟踪_详解JVM中的本机内存跟踪

    1.概述 有没有想过为什么Java应用程序通过众所周知的-Xms和-Xmx调优标志消耗的内存比指定数量多得多?出于各种原因和可能的优化,JVM可以分配额外的本机内存.这些额外的分配最终会使消耗的内存超 ...

  5. visual studio内存溢出检测工具_详解JVM内存管理与垃圾回收机制2 何为垃圾

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背 ...

  6. java 多态实现的jvm调用过程_多态:JVM是如何进行方法调用的

    在我们平时的工作学习中写java代码时,如果我们在同一个类中定义了两个方法名和参数类型都相同的方法时,编译器会直接报错给我们.还有在代码运行的时候,如果子类定义了一个与父类完全相同的方法的时候,父类的 ...

  7. rhel系统启动过程_详解linux系统的启动过程及系统初始化

    一.linux系统进程启动流程图: 二.简单概括描述linux系统从开机到登陆界面的启动过程 1.开机BIOS自检 2.MBR引导 3.grub引导菜单 4.加载内核 5.启动init进程 6.读取i ...

  8. JVM垃圾清理机制详解 ✨ 每日积累

    JVM垃圾清理机制详解 jvm内存结构中有一块地方叫做堆内存,里面存放着我们应用创建的对象,但是我们堆内存有限,对象在运行的时候持续创建,jvm有垃圾清理机制来清理对象确保堆内存的可用空间. 清理流程 ...

  9. 详解JVM类加载机制

    详解JVM类加载机制 笔者的笔记都记录在有道云里面,因为公司原因办公电脑无法使用有道云,正好借此机会整理下以前的笔记顺便当做巩固复习了,也因为记笔记的时候不会记录这些知识来源何地,所以如果发现原创后可 ...

最新文章

  1. lintcode---线段树查询||(区间元素个数)
  2. webpack入门(六)——html-webpack-plugin
  3. 外卖行业现状分析_2019年中国外卖行业市场现状与发展趋势分析 用户市场渐趋下沉【组图】...
  4. 无处不在的 AWS 云计算
  5. web窗体的基本控件
  6. 判别模型的玻尔兹曼机论文源码解读
  7. java concurrent 锁_java并发机制锁的类型和实现
  8. mysql排序行号_mysql 取得行号后再排序
  9. hive架构及使用场景
  10. SVN的配置与使用方法
  11. 框架神器:struts2标签库
  12. 11.企业应用架构模式 --- 对象-关系行为模式
  13. StringUtil工具类:
  14. UGUI适配问题 1.UI坐标与屏幕坐标转换
  15. android模拟器登录qq,手机 上来 个自动 Appium+Python3+夜神安卓模拟器 实现QQ自动登录...
  16. 上海驾照科目三练习笔记
  17. Web Polygraph 安装
  18. 如何将各种电子书格式转换为PDF格式
  19. 蓝牙资讯|Q2全球TWS耳机出货量排行出炉,蓝牙音频新技术将推出市场
  20. Bose Soundlink Ⅲ 随机断电故障处理

热门文章

  1. Docker映像存储在哪里? Docker容器路径介绍
  2. 前端拆分_如何在消费者驱动的合同测试的帮助下拆分前端和后端的部署
  3. firebase使用_如何开始使用Firebase Hosting
  4. ubuntu18.04安裝搜狗輸入法
  5. simulink模块使用记录2-EnabledSubsystem/merge
  6. Hive UDF 中使用hdfs中的文件
  7. 解决WARN TaskSchedulerImpl: Initial job has not accepted any resources;
  8. 原版英文书籍《Linux命令行》阅读记录6 | 重定向
  9. Python基础之迭代器
  10. Team Foundation Server 2010 安装、部署与配置(七):创建 Team Project 时的一个 Issue:TF218027 .