Java类加载和对象创建
引言
Java代码需要被使用,必须要经过类加载器加载到内存中,然后对应的类才能够被创建使用,这文对类加载和对象创建和过程进行分析。
类加载
Java类通过懒加载的方式,经过了Loading、Linking、Initializing后加载到内存中,才能被进行使用。

Loading
懒加载
Java类并不是JVM虚拟机启动的时候,就对所有用到的类进行全部加载,而是在第一次使用到的时候,进行加载
LazyLoading五种情况
- new getstatic putstatic invokestatic指令,访问final变量除外
- java.lang.reflect对类进行反射调用时
- 初始化子类的时候,父类首先初始化
- 虚拟机启动时,被执行的主类必须初始化
- 动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic的方法句柄时,该类必须初始化
双亲委派
Java类加载的时候,默认情况下,遵循双亲委派的原则(自下向上的查找,自顶向下的加载),为了类安全,核心类不会遭到破坏

ClassLoader源码
ClassLoader三个步骤
findInCache -> parent.loadClass -> findClass()
在抽象类ClassLoader里面,通过模板方法的方式,给出了默认未重写情况下的LoadClass方法
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
//先在自己的缓存中查找是否已经加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//如果自己没有加载过,就交给父类去加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//Bootstrap加载器的parent变量为空,当发现parent为空时,则使用bootstrap自己去加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//当交给父类加载器去加载后,如果没有加载到返回了NULL,则自己去进行加载
//这个地方会报ClassNotFound异常,但如果还有子加载器的话,则会在上面被捕捉到,如果已经是最子级加载器的话,则直接报异常。
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//进行解析
resolveClass(c);
}
//返回调用的地方
return c;
}
}
一般情况下,不需要打破双亲委派的,在自定义ClassLoader的时候,只需要重写findClass方法即可。
自定义ClassLoader
- 继承ClassLoader
- 重写findClass方法
将字节码文件读入到内存,将其转换成Byte数据,通过defineClass将其转换成对应的Class对象。
FileInputStream -- read进内存 -> 写进ByteArrayOutputStream,再转换成Byte数组
默认的情况下,自定义ClassLoader的parent字段是AppClassLoader,如果没有重写LoadClass方法,则依然遵循双亲委派的加载原则。
思考:如何打破双亲委派原则
通过重写loadClass方法即可打破双亲委派的原则
思考:什么情况下需要打破双亲委派
- JDK1.2之前,自定义ClassLoader都必须重写loadClass()
- ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定、
- 热启动,热部署,tomcat有自己的模块指定classloader(可以加载同一类库的不同版本),还可以实现不同Web Context进行类隔离
Linking
Linking的时候,实际是有三个步骤
Verification(校验)
验证文件是否符合JVM规定
Preparation(准备)
将类的静态变量赋默认值
Resolution(解析)
将类、方法、属性等符号引用解析为直接引用
常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
Initializing
对类进行初始化,调用类初始化代码 clinit,给静态成员变量赋初始值,按顺序执行
对象创建
申请对象内存

图中可以看到,优先在栈上分配->TLAB->老年代->Eden区
栈上分配:对象生命周期随着方法的调用开始而开始,结束而结束,不需要进行垃圾回收减轻了GC的负担
TLAB: 线程私有的堆内存,可以很好的避免了共享堆中多个线程操作堆内存分配对象空间时产生的同步问题(虽然有CAS+失败重试),从而提高堆上对象分配的效率。成员变量赋默认值
调用构造方法init(成员变量赋初始值,调用类的相应的构造方法)

JVM执行模式
解释模式
- 通过解释器(Bytecode Interpreter)解释执行
- 特点:启动快(不需要编译),执行慢
- 可通过-Xint参数指定为纯解释模式
编译模式
- 由JIT(Just In-Time Compiler)编译为本地代码(C语言实现)执行
- 特点:启动慢(编译过程较慢),执行快
- 可通过-Xcomp参数指定为纯编译模式
混合模式
- 混合使用解释器 + 热点代码编译
- 起始阶段采用解释执行
- 热点代码检测(HotSpot),默认-XX:CompileThreshold=10000
- 多次被调用的方法(方法计数器:监测方法执行频率)
- 多次被调用的循环(循环计数器:监测循环执行频率)
- 对热点代码进行编译
- 默认采用这种模式,可通过-Xmixed指定
Java类加载和对象创建的更多相关文章
- java类加载、对象创建过程
类加载过程: 1, JVM会先去方法区中找有没有相应类的.class存在.如果有,就直接使用:如果没有,则把相关类的.class加载到方法区 2, 在.class加载到方法区时,会分为两部分加载:先加 ...
- java基础---类加载和对象创建过程
类中可以存在的成员: class A{ 静态成员变量: 非静态成员变量: 静态函数: 非静态函数: 构造函数 A(..){...} 静态代码块 static{...} 构造代码块 {...} } 类加 ...
- Java 虚拟机的对象创建
堆中存储的内容:在程序运行时,动态创建的对象. 创建对象的四种方式:new,clone(浅复制),反射,反序列化. 浅复制:只能复制当前对象本身,如果当前对象(A)引用了另外的对象(B),则引用对象( ...
- Java虚拟机(二)-对象创建
这一篇大致说明一下,对象在Java堆中对象分配.内存布局以及访问定位 1.对象的创建 虚拟机在遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引 ...
- java类加载和对象初始化
对象初始化过程: 1.首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化: 2.然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化: 3.其次,初 ...
- Java中String对象创建机制详解()
一String 使用 private final char value来实现字符串存储 二Java中String的创建方法四种 三在深入了解String创建机制之前要先了解一个重要概念常量池Const ...
- JAVA类和对象创建
面向对象 学习目标: 理解面向对象的编程思想 理解类与对象的关系 如何创建类与对象 方法重载 一:什么是面向对象编程(OOP) 面向对象编程(Object Oriented Programming,O ...
- Java中类加载过程和对象创建过程
类加载过程: 1, JVM会先去方法区中找有没有相应类的.class存在.如果有,就直接使用:如果没有,则把相关类的.class加载到方法区 2, 在.class加载到方法区时,会分为两部分加载:先加 ...
- Java中对象创建过程
本文介绍的对象创建过程仅限于普通Java对象,不包括数组和Class对象. 1.类加载检查 虚拟机遇到一条new指令时,首先去检查该指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用 ...
- 【Java基础】Java类的加载和对象创建流程的详细分析
相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 package ...
随机推荐
- 深度解读 OpenYurt:从边缘自治看 YurtHub 的扩展能力
作者 | 新胜 阿里云技术专家 导读:OpenYurt 开源两周以来,以非侵入式的架构设计融合云原生和边缘计算两大领域,引起了不少行业内同学的关注.阿里云推出开源项目 OpenYurt,一方面是把阿 ...
- Hologres+Hologres+Flink流批一体首次落地4982亿背后的营销分析大屏Flink流批一体首次落地4982亿背后的营销分析大屏
简介: 本篇将重点介绍Hologres在阿里巴巴淘宝营销活动分析场景的最佳实践,揭秘Flink+Hologres流批一体首次落地阿里双11营销分析大屏背后的技术考验. 概要:刚刚结束的2020天猫双1 ...
- Kubernetes 稳定性保障手册 -- 可观测性专题
简介: 伴随大家对稳定性重视程度的不断提升.社区可观测性项目的火热,可观测性成为了一个很热门的话题,站在不同的角度会产生不同的理解. 我们从软件开发的生命周期出发,尝试形成对可观测性的一个宏观理解,并 ...
- iofsstat:帮你轻松定位 IO 突高,前因后果一目了然 | 龙蜥技术
简介:磁盘被打满到底是真实的业务需求量上来了呢?还是有什么野进程在占用 IO? iofsstat 帮你精准定位. 编者按:sysAK(system analyse kit),是龙蜥社区系统运维 SI ...
- 供应链商品域DDD实践
简介: DDD是一套方法论,实践能否成功,不仅仅是个技术问题,更是执行贯彻实施的问题.本文将就DDD的基本概念和DDD的实施进行分享. 作者 | 侧帽来源 | 阿里技术公众号 前言 供应链商品域DDD ...
- 七天.NET 8操作SQLite入门到实战 - (2)第七天Blazor班级管理页面编写和接口对接
前言 上一章节我们引入BootstrapBlazor UI组件完成了EasySQLite后台界面的基本架子的搭建,本章节的主要内容是Blazor班级管理页面编写和接口对接. 七天.NET 8 操作 S ...
- 开发日记:中控PUSH协议
using System; using System.IO; using System.Net; using System.Text.RegularExpressions; namespace Con ...
- 您可知道如何通过`HTTP2`实现TCP的内网穿透???
可能有人很疑惑应用层 转发传输层?,为什么会有这样的需求啊???哈哈技术无所不用其极,由于一些场景下,对于一个服务器存在某一个内部网站中,但是对于这个服务器它没有访问外网的权限,虽然也可以申请端口访问 ...
- gorm 关系一对一,一对多,多对多查询
gorm 关系一对一,一对多,多对多查询 gorm v2版本 Belongs To mysql表 CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_IN ...
- vue3组件封装
1.父组件调用子组件属性和方法 父组件中template写法: <role-modal ref="myRoleModal" @OK="roleModalOK&quo ...