引言

Java代码需要被使用,必须要经过类加载器加载到内存中,然后对应的类才能够被创建使用,这文对类加载和对象创建和过程进行分析。

类加载

Java类通过懒加载的方式,经过了Loading、Linking、Initializing后加载到内存中,才能被进行使用。

Loading

懒加载

Java类并不是JVM虚拟机启动的时候,就对所有用到的类进行全部加载,而是在第一次使用到的时候,进行加载

LazyLoading五种情况

  1. new getstatic putstatic invokestatic指令,访问final变量除外
  2. java.lang.reflect对类进行反射调用时
  3. 初始化子类的时候,父类首先初始化
  4. 虚拟机启动时,被执行的主类必须初始化
  5. 动态语言支持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

  1. 继承ClassLoader
  2. 重写findClass方法

    将字节码文件读入到内存,将其转换成Byte数据,通过defineClass将其转换成对应的Class对象。

    FileInputStream -- read进内存 -> 写进ByteArrayOutputStream,再转换成Byte数组

    默认的情况下,自定义ClassLoader的parent字段是AppClassLoader,如果没有重写LoadClass方法,则依然遵循双亲委派的加载原则。

思考:如何打破双亲委派原则

通过重写loadClass方法即可打破双亲委派的原则

思考:什么情况下需要打破双亲委派

  1. JDK1.2之前,自定义ClassLoader都必须重写loadClass()
  2. ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定、
  3. 热启动,热部署,tomcat有自己的模块指定classloader(可以加载同一类库的不同版本),还可以实现不同Web Context进行类隔离

Linking

Linking的时候,实际是有三个步骤

Verification(校验)

验证文件是否符合JVM规定

Preparation(准备)

将类的静态变量赋默认值

Resolution(解析)

将类、方法、属性等符号引用解析为直接引用

常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

Initializing

对类进行初始化,调用类初始化代码 clinit,给静态成员变量赋初始值,按顺序执行

对象创建

  1. 申请对象内存



    图中可以看到,优先在栈上分配->TLAB->老年代->Eden区

    栈上分配:对象生命周期随着方法的调用开始而开始,结束而结束,不需要进行垃圾回收减轻了GC的负担

    TLAB: 线程私有的堆内存,可以很好的避免了共享堆中多个线程操作堆内存分配对象空间时产生的同步问题(虽然有CAS+失败重试),从而提高堆上对象分配的效率。

  2. 成员变量赋默认值

  3. 调用构造方法init(成员变量赋初始值,调用类的相应的构造方法)

JVM执行模式

解释模式

  1. 通过解释器(Bytecode Interpreter)解释执行
  2. 特点:启动快(不需要编译),执行慢
  3. 可通过-Xint参数指定为纯解释模式

编译模式

  1. 由JIT(Just In-Time Compiler)编译为本地代码(C语言实现)执行
  2. 特点:启动慢(编译过程较慢),执行快
  3. 可通过-Xcomp参数指定为纯编译模式

混合模式

  1. 混合使用解释器 + 热点代码编译
  2. 起始阶段采用解释执行
  3. 热点代码检测(HotSpot),默认-XX:CompileThreshold=10000
    • 多次被调用的方法(方法计数器:监测方法执行频率)
    • 多次被调用的循环(循环计数器:监测循环执行频率)
  4. 对热点代码进行编译
  5. 默认采用这种模式,可通过-Xmixed指定

Java类加载和对象创建的更多相关文章

  1. java类加载、对象创建过程

    类加载过程: 1, JVM会先去方法区中找有没有相应类的.class存在.如果有,就直接使用:如果没有,则把相关类的.class加载到方法区 2, 在.class加载到方法区时,会分为两部分加载:先加 ...

  2. java基础---类加载和对象创建过程

    类中可以存在的成员: class A{ 静态成员变量: 非静态成员变量: 静态函数: 非静态函数: 构造函数 A(..){...} 静态代码块 static{...} 构造代码块 {...} } 类加 ...

  3. Java 虚拟机的对象创建

    堆中存储的内容:在程序运行时,动态创建的对象. 创建对象的四种方式:new,clone(浅复制),反射,反序列化. 浅复制:只能复制当前对象本身,如果当前对象(A)引用了另外的对象(B),则引用对象( ...

  4. Java虚拟机(二)-对象创建

    这一篇大致说明一下,对象在Java堆中对象分配.内存布局以及访问定位 1.对象的创建 虚拟机在遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引 ...

  5. java类加载和对象初始化

    对象初始化过程:  1.首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化:  2.然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化:  3.其次,初 ...

  6. Java中String对象创建机制详解()

    一String 使用 private final char value来实现字符串存储 二Java中String的创建方法四种 三在深入了解String创建机制之前要先了解一个重要概念常量池Const ...

  7. JAVA类和对象创建

    面向对象 学习目标: 理解面向对象的编程思想 理解类与对象的关系 如何创建类与对象 方法重载 一:什么是面向对象编程(OOP) 面向对象编程(Object Oriented Programming,O ...

  8. Java中类加载过程和对象创建过程

    类加载过程: 1, JVM会先去方法区中找有没有相应类的.class存在.如果有,就直接使用:如果没有,则把相关类的.class加载到方法区 2, 在.class加载到方法区时,会分为两部分加载:先加 ...

  9. Java中对象创建过程

    本文介绍的对象创建过程仅限于普通Java对象,不包括数组和Class对象. 1.类加载检查 虚拟机遇到一条new指令时,首先去检查该指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用 ...

  10. 【Java基础】Java类的加载和对象创建流程的详细分析

    相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 package ...

随机推荐

  1. Apsara Stack 技术百科 | 可运营的行业云,让云上资源跑起来

    ​简介:企业级云管理平台,如何打造千人千面的个性化体验,从应用.云资源.硬件等进行全局智能优化,实现资源配置的最佳配比,构建精细化运营能力? ​ 距离第一例新冠疫情病例的发现,不知不觉中已经过去两年, ...

  2. 【ESSD技术解读-01】 云原生时代,阿里云 ESSD 快照服务 助力企业级数据保护

    ​简介:本文以云原生为时代背景,介绍了阿里云块存储快照服务如何基于高性能 ESSD 云盘提升快照服务性能,提供轻量.实时的用户体验及揭秘背后的技术原理.依据行业发展及云上数据保护场景,为企业用户及备份 ...

  3. Ollama是什么,为什么这么好用

    Ollama 是一个开源框架,专为在本地机器上便捷部署和运行大型语言模型(LLM)而设计. 以下是其主要特点和功能概述: 1. 简化部署:Ollama 目标在于简化在 Docker 容器中部署大型语言 ...

  4. [PHP] 业务逻辑大内存占用的优化思路, yield 和 chunk

      示例: header("content-type:text/html;charset=utf-8"); function readTxt() { $handle = fopen ...

  5. Partition和ReduceTask的关系

    先看源码: numPartitions = conf.getNumReduceTasks(); if (numPartitions > 1) { //设置了ReduceTask个数后(大于1), ...

  6. WordPress CVE-2022-4230复现分析

    前言 开始CVE审计之旅 WP Statistics WordPress 插件13.2.9之前的版本不会转义参数,这可能允许经过身份验证的用户执行 SQL 注入攻击.默认情况下,具有管理选项功能 (a ...

  7. 联想G470安装黑苹果

    macos10136 黑苹果usb无线网卡 1.系统下载: 下面是我自制的带clover 4596版本的u盘镜像: 链接: https://pan.baidu.com/s/1wRdVddwkei7bf ...

  8. 安装XMind如何安装到指定目录

    在Win10系统上安装XMind,发现安装完成之后,XMind被安装到了C盘.由于C盘是系统盘,这让人很不爽.XMind在安装过程中也没有提供安装路径选择,而是点击安装程序之后,就一路开始安装到C盘. ...

  9. js不同类型比较

    有布尔 先把布尔转为number 数字和字符串 字符串转number,如果前导为0会被忽略,空字符串转换成0,非数字字符串或其他转为NaN 对象和非对象 对象valueOf获取基本类型,对象转为字符串 ...

  10. C#.Net筑基-模式匹配汇总

    01.模式匹配概述 从C#7开始支持的 模式匹配 语法(糖,挺甜),可非常灵活的对数据进行条件匹配和提取,经过多个版本的完善,已经非常强大了. C# 支持多种模式,包括声明.类型.常量.关系.属性.列 ...