`

Java程序员从笨鸟到菜鸟之(九十三)深入java虚拟机(二)——类的生命周期(上)类的加载和连接

阅读更多

 

本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188

类加载器,顾名思义,类加载器(classloader )用来加载 Java 类到 Java 虚拟机中。一般来说, Java 虚拟机使用 Java 类的方式如下: Java 源程序( .java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码( .class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance() 方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。基本上所有的类加载器都是 java.lang.ClassLoader 类的一个实例。 其实我们研究类加载器主要研究的就是类的生命周期


 

先来了解一下jvm java虚拟机 )中的几个比较重要的内存区域,这几个区域在java 类的生命周期中扮演着比较重要的角色:


方法区: java 的虚拟机中有一块专门用来存放已经加载的类信息、常量、静态变量以及方法代码的内存区域,叫做方法区。


常量池: 常量池是方法区的一部分,主要用来存放常量和类中的符号引用等信息。


堆区: 用于存放类的对象实例。


栈区: 也叫java 虚拟机栈,是由一个一个的栈帧组成的后进先出的栈式结构,栈桢中存放方法运行时产生的局部变量、方法出口等信息。当调用一个方法时,虚拟机栈中就会创建一个栈帧存放这些数据,当方法调用完成时,栈帧消失,如果方法中调用了其他方法,则继续在栈顶创建新的栈桢。

类的生命周期
当我们编写一个java 的源文件后,经过编译会生成一个后缀名为 class 的文件,这种文件叫做字节码文件,只有这种字节码文件才能够在 java 虚拟机中运行, java 类的生命周期就是指一个 class 文件从加载到卸载的全过程。一个 java 类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五个阶段,当然也有在加载或者连接之后没有被初始化就直接被使用的情况, 这里我们主要来研究类加载器所执行的部分,也就是加载,链接和初始化。如图所示:


下面我先简单看一下类加载器所执行的三部分的简单介绍
1 加载:查找并加载类的二进制数据

2、连接

–验证:确保被加载的类的正确性

–准备:为类的静态变量分配内存,并将其初始化为默认值

–解析:把类中的符号引用转换为直接引用

3、初始化:为类的静态变量赋予正确的初始值

从上边我们可以看出类的静态变量赋了两回值。这是为什么呢?原因是,在连接过程中时为静态变量赋值为默认值,也就是说,只要是你定义了静态变量,不管你开始给没给它设置,我系统都为他初始化一个默认值。到了初始化过程,系统就检查是否用户定义静态变量时有没有给设置初始化值,如果有就把静态变量设置为用户自己设置的初始化值,如果没有还是让静态变量为初始化值

 

类的加载、连接和初始化


 

 

类的加载

 

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。这里的class对象其实就像一面镜子一样,外面是类的源程序,里面是class对象,它实时的反应了类的数据结构和信息。

 

加载.class文件的方式

1 从本地系统中直接加载

2 通过网络下载.class文件

3 从zip,jar等归档文件中加载.class文件

4 从专有数据库中提取.class文件

5 将Java源文件动态编译为.class文件

 

类的加载过程

 

 

结论:

1 类的加载的最终产品是位于堆区中的Class对象

2 Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口

 

Java虚拟机给我们提供了两种类加载器:

 

 

1、Java虚拟机自带的加载器

1)根类加载器(使用C++编写,程序员无法在Java代码中获得该类)

2)扩展加载器,使用Java代码实现

3)系统加载器(应用加载器),使用Java代码实现

2、用户自定义的类加载器

java.lang.ClassLoader的子类

用户可以定制类的加载方式


我们看一下API ClassLoader的介绍:

类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。如果给定类 的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的 类文件 。每个 class对象都包含一个对定义它的ClassLoader 的引用。

我们再来看一下Class类的一个方法 getClassLoader

publicClassLoader getClassLoader()

返回该类的类加载器。有些实现可能使用null 来表示 根类加载器。如果该类由根类加载器加载,则此方法在这类实现中将返回null


下面我们来看一个小例子来验证一下:

 

package com.bzu.csh;
public class Test
{
public static void main(String[] args) throws Exception
{
Class clazz = Class.forName("java.lang.String");
System.out.println(clazz.getClassLoader());
Class clazz2 = Class.forName("com.bzu.csh.ABC");
System.out.println(clazz2.getClassLoader());
}
}
class ABC
{
}


 

看一下打印结果,一目了然:


 

从上面打印结果可以看出,第一个为null ,也就是它用根类加载器加载的,第二个是我们自己写的类,也就是说,我们自己写的那个类用 sun.misc.Launcher$AppClassLoader@1372a1a加载器加载的,我们可以看到APP,也就是应用类加载器,也就是系统加载器


还记得我们以前用过的动态代理吧,InvocationHandler ,当我们利用 proxy 对象调用 newProxyInstance建立一个代理类时,我们要给他传一个ClassLoader ,也就是类加载器,如下:

 

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException


 

当时我们学习的时候,只知道这里的loader随便给他设置一个类的类加载器就可以。现在我们来想想为什么这里需要一个类加载器呢?我们知道这个 newProxyInstance是动态的给我们生成一个代理类,然后根据这个代理类生成一个代理对象。动态生成这个代理类之后我们不得把他加载到内存里吗,加载到内存里我们才可以用他。用什么加载到内存里,只有类加载器,所以我们要给他指定一个类加载器。


类加载器并不需要等到某个类被 首次主动使用 时再加载它 。JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。大家在做web开发的时候有可能会出现这种问题,比如我们在做测试的时候是用的jdk1.6,而我们在部署的时候我们用的是jdk1.5.这时候就很可能汇报LinkageError错误,版本不兼容。


 

类的连接

 

 

类被加载后,就进入连接阶段。连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。

验证 一个类被加载之后,必须要验证一下这个类是否合法,比如这个类是不是符合字节码的格式、变量与方法是不是有重复、数据类型是不是有效、继承与实现是否合乎标准等等。总之,这个阶段的目的就是保证加载的类是能够被jvm 所运行。 很多人都感觉,既然这个类都通过编译加载到内存里了,那肯定就是合法的了,为什么还要验证呢,这是因为这里的验证时为了避免有人恶意编写class 文件,也就是说并不是通过编译得到的 class 文件。所以这里验证其实是检查的 class 文件的内部结构是否符合字节码的要求


准备 备阶段的工作就是为类的静态变量分配内存并设为jvm 默认的初值,对于非静态的变量,则不会为它们分配内存。有一点需要注意,这时候,静态变量的初值为 jvm 默认的初值,而不是我们在程序中设定的初值。 jvm 默认的初值是这样的:
基本类型(int long short char byte boolean float double )的默认值为 0
引用类型的默认值为null
常量的默认值为我们程序中设定的值,比如我们在程序中定义finalstaticinta=100 ,则准备阶段中 a 的初值就是 100


解析: 一阶段的任务就是把常量池中的符号引用转换为直接引用。那么什么是符号引用,什么又是直接引用呢?我们来举个例子:我们要找一个人,我们现有的信息是这个人的身份证号是1234567890 。只有这个信息我们显然找不到这个人,但是通过公安局的身份系统,我们输入 1234567890 这个号之后,就会得到它的全部信息:比如 山东省滨州市滨城区18号张三,通过这个信息我们就能找到这个人了。这里, 123456790 就好比是一个符号引用,而 山东省滨州市滨城区18号张三就是直接引用。在内存中也是一样,比如我们要在内存中找一个类里面的一个叫做 show 的方法,显然是找不到。但是在解析阶段, jvm 就会把 show 这个名字转换为指向方法区的的一块内存地址,比如 c17164 ,通过 c17164 就可以找到 show 这个方法具体分配在内存的哪一个区域了。这里 show 就是符号引用,而 c17164 就是直接引用。在解析阶段, jvm 会将所有的类或接口名、字段名、方法名转换为具体的内存地址。


下一篇内容继续讲解类加载器内容。。。。

 

 

 

 

参考文章: http://www.2cto.com/kf/201204/129386.html

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    毕业设计基于STC12C5A、SIM800C、GPS的汽车防盗报警系统源码.zip

    STC12C5A通过GPS模块获取当前定位信息,如果车辆发生异常震动或车主打来电话(主动请求定位),将通过GSM发送一条定位短信到车主手机,车主点击链接默认打开网页版定位,如果有安装高德地图APP将在APP中打开并展示汽车当前位置 GPS模块可以使用多家的GPS模块,需要注意的是,当前程序对应的是GPS北斗双模芯片,故只解析 GNRMC数据,如果你使用GPS芯片则应改为GPRMC数据即可。 系统在初始化的时候会持续短鸣,每初始化成功一部分后将长鸣一声,如果持续短鸣很久(超过20分钟),建议通过串口助手查看系统输出的调试信息,系统串口默认输出从初始化开始的所有运行状态信息。 不过更建议你使用SIM868模块,集成GPS.GSM.GPRS,使用更加方便

    基于tensorflow2.x卷积神经网络字符型验证码识别.zip

    基于tensorflow2.x卷积神经网络字符型验证码识别 卷积神经网络(Convolutional Neural Networks, CNNs 或 ConvNets)是一类深度神经网络,特别擅长处理图像相关的机器学习和深度学习任务。它们的名称来源于网络中使用了一种叫做卷积的数学运算。以下是卷积神经网络的一些关键组件和特性: 卷积层(Convolutional Layer): 卷积层是CNN的核心组件。它们通过一组可学习的滤波器(或称为卷积核、卷积器)在输入图像(或上一层的输出特征图)上滑动来工作。 滤波器和图像之间的卷积操作生成输出特征图,该特征图反映了滤波器所捕捉的局部图像特性(如边缘、角点等)。 通过使用多个滤波器,卷积层可以提取输入图像中的多种特征。 激活函数(Activation Function): 在卷积操作之后,通常会应用一个激活函数(如ReLU、Sigmoid或tanh)来增加网络的非线性。 池化层(Pooling Layer): 池化层通常位于卷积层之后,用于降低特征图的维度(空间尺寸),减少计算量和参数数量,同时保持特征的空间层次结构。 常见的池化操作包括最大池化(Max Pooling)和平均池化(Average Pooling)。 全连接层(Fully Connected Layer): 在CNN的末端,通常会有几层全连接层(也称为密集层或线性层)。这些层中的每个神经元都与前一层的所有神经元连接。 全连接层通常用于对提取的特征进行分类或回归。 训练过程: CNN的训练过程与其他深度学习模型类似,通过反向传播算法和梯度下降(或其变种)来优化网络参数(如滤波器权重和偏置)。 训练数据通常被分为多个批次(mini-batches),并在每个批次上迭代更新网络参数。 应用: CNN在计算机视觉领域有着广泛的应用,包括图像分类、目标检测、图像分割、人脸识别等。 它们也已被扩展到处理其他类型的数据,如文本(通过卷积一维序列)和音频(通过卷积时间序列)。 随着深度学习技术的发展,卷积神经网络的结构和设计也在不断演变,出现了许多新的变体和改进,如残差网络(ResNet)、深度卷积生成对抗网络(DCGAN)等。

    【三维装箱】遗传和模拟退火算法求解三维装箱优化问题【含Matlab源码 031期】.zip

    【三维装箱】遗传和模拟退火算法求解三维装箱优化问题【含Matlab源码 031期】.zip

    自己编写的python 程序计算cpk/ppk

    cpk&ppk python 小程序,品友点评

    基于Springboot开发的分布式抽奖系统.zip

    基于springboot的java毕业&课程设计

    课设毕设基于SpringBoot+Vue的影城管理系统 LW+PPT+源码可运行.zip

    课设毕设基于SpringBoot+Vue的影城管理系统 LW+PPT+源码可运行.zip

    MC教育版(免登录版)

    MC教育版(免登录版)

    农作物叶片病害分类和分割数据集【数据集+标签】

    包含13993张数据和对应的13993张mask分割模版,数据集用不同目录保存,也可以用作分类数据集 类别包含:桃子、辣椒、覆盆子、大豆、南瓜、草莓

    基于vue+springboot二手交易网站.zip

    基于springboot的java毕业&课程设计

    【三维装箱】遗传算法求解三维装箱优化问题【含Matlab源码 3408期】.zip

    【三维装箱】遗传算法求解三维装箱优化问题【含Matlab源码 3408期】.zip

    基于javaspring 开发框架的培训教程 TP1.zip

    基于javaspring 开发框架的培训教程 TP1.zip

    信号和系统 MATLAB 代码:探索信号生成、系统建模、傅立叶分析、滤波器设计、采样和控制系统仿真.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    医院网上预约系统设计与开发/毕业设计

    医院网上预约系统设计与开发/毕业设计 JSP基于SSM网上医院预约挂号系统 科室信息: 科室id,科室名称,科室介绍,成立日期,负责人 医生信息: 医生工号,登录密码,所在科室,医生姓名,性别,医生照片,出生日期,医生职位,工作经验,联系方式,擅长,医生介绍 病人信息: 病人id,医生,病人姓名,性别,身份证号,联系电话,病人病例,登记时间 预约信息: 预约id,预约用户,预约医生,预约日期,时段,联系电话,下单时间,处理状态,医生回复 新闻信息: 新闻id,新闻标题,新闻图片,新闻内容,新闻日期,新闻来源 留言: 留言id,留言标题,留言内容,留言人,留言时间,管理回复,回复时间 用户: 用户名,登录密码,姓名,性别,出生日期,用户照片,联系电话,邮箱,家庭地址,注册时间

    企业数据治理之数据质量治理方案.pptx

    企业数据治理之数据质量治理方案

    MySQL8.4.0 LTS(mysql-8.4.0-10.fc38.x86-64.rpm-bundle.tar)

    MySQL8.4.0 LTS(mysql-8.4.0-10.fc38.x86_64.rpm-bundle.tar)适用于Linux Fedora 38 (x86, 64-bit)

    1659 jsp游乐园管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目源码

    一、源码特点 java 医疗数据管理系统 是一套完善的web设计系统,对理解JSP java编程开发语言有帮助采用了数据模型进行区块链设计,系统具有完整的源代码和数据库,系统采用web模式,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql,使用java语言开发。 二、功能介绍 如博客中介绍 三、注意事项 1、管理员账号:admin 密码:admin 数据库配置文件DBO.java ,权限包括管理员,用户 2、开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql,使用java语言开发。 3、数据库文件名是jspyly 系统名称yly 4、地址:http://127.0.0.1:8080/yly/qt/index.jsp

    基于SpringBoot 搭建的番茄短视频-后台管理系统.zip

    基于springboot的java毕业&课程设计

    54.医院分诊管理系统的设计与实现-基于SSM框架+ Mysql+Java设计与实现(可运行源码+数据库+lw)毕业设计管理系统

    可运行源码(含数据库脚本)+开发文档+lw(高分毕设项目) java期末大作业毕业设计项目管理系统计算机软件工程大数据专业 内容概要:通过陆丰市医院门诊分诊系统设计的研究背景、研究的意义和目的,通过运用java语言和ssm框架来建立一款分诊管理系统,能够帮助医院提高工作效率,减少工作中出现的错误率。设计出挂号管理、排队候诊管理以及叫号管理等多个子模块,绘制出实体关系图,利用MySQL技术建立数据库达成了软件与数据库的互通。最后对工作进行了总结和展望。 关键词:分诊管理系统;功能建模;java 全套项目源码+详尽文档,一站式解决您的学习与项目需求。 适用人群: 计算机、通信、人工智能、自动化等专业的学生、老师及从业者。 使用场景及目标: 无论是毕设、期末大作业还是课程设计,一键下载,轻松部署,助您轻松完成项目。 项目代码经过调试测试,确保直接运行,节省您的时间和精力。 其他说明: 项目整体具有较高的学习借鉴价值,基础能力强的可以在此基础上修改调整,以实现不同的功能。

    基于Springboot和SpringCloud的博客.zip

    基于springboot的java毕业&课程设计

Global site tag (gtag.js) - Google Analytics