echart实例数据 本地加载_JVM 类加载概述

来源:SegmentFault 思否社区

作者:又坏又迷人


JVM简介

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。


内存结构概述

0faf1046d533fbc926e2849547e7c402.png

448f134c7dad2df4547b75dd1178cd69.png

类加载子系统(Class Loader)

类加载器分为:自定义类加载器 < 系统类加载器 < 扩展类加载器 < 引导类加载器

类加载过程分为:加载、链接、验证、初始化。

程序计数器(Program Counter Register)

是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器,指向下一个将要执行的指令代码,由执行引擎来读取下一条指令。

虚拟机栈 (Stack Area)

栈是线程私有,栈帧是栈的元素。每个方法在执行时都会创建一个栈帧。栈帧中存储了局部变量表、操作数栈、动态连接和方法出口等信息。每个方法从调用到运行结束的过程,就对应着一个栈帧在栈中压栈到出栈的过程。

本地方法栈 (Native Method Area)

JVM 中的栈包括 Java 虚拟机栈和本地方法栈,两者的区别就是,Java 虚拟机栈为 JVM 执行 Java 方法服务,本地方法栈则为 JVM 使用到的 Native 方法服务。

堆 (Heap Area)

堆是Java虚拟机所管理的内存中最大的一块存储区域。堆内存被所有线程共享。主要存放使用new关键字创建的对象。所有对象实例以及数组都要在堆上分配。垃圾收集器就是根据GC算法,收集堆上对象所占用的内存空间。

Java堆分为年轻代(Young Generation)和老年代(Old Generation);年轻代又分为伊甸园(Eden)和幸存区(Survivor区);幸存区又分为From Survivor空间和 To Survivor空间。

方法区(Method Area)

方法区同 Java 堆一样是被所有线程共享的区间,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。更具体的说,静态变量+常量+类信息(版本、方法、字段等)+运行时常量池存在方法区中。常量池是方法区的一部分。

JDK 8 使用元空间 MetaSpace 代替方法区,元空间并不在JVM中,而是在本地内存中

类加载过程概述

类加载器子系统负责从文件系统或者网络中在家Class文件,class文件在文件开头又特定的文件标识。

ClassLoader只负责class文件的加载,至于它是否可以运行,则由ExecutionEngine决定。

加载类的信息存放于一块被称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)


类加载器ClassLoader角色

4a24fb4ae354239163bf445a2db3348a.png

  1. class文件存在本地硬盘上,在执行时加载到JVM中,根据这个文件可以实例化出n个一模一样的实例。
  2. class文件加载到JVM中,被称为DNA元数据模板,放在方法区中。
  3. 在.class文件 -> JVM -> 最终成为元数据模板的过程中,ClassLoader就扮演一个快递员的角色。


类加载过程概述

7f24e4245c24fabd81fcd3b16879cdac.png

8e872d3afa98e83c0a8ccb0293490687.png

7c0bff82937f54724bb06ec14c043148.png

类的加载过程大致分为三个阶段:加载,链接,初始化。


类的加载过程一:加载(Loading)

  1. 通过一个类的全限定名来获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

类的加载过程二:链接(Linking)

验证(Verify)

  1. 目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害到虚拟机的安全。

准备(Prepare)

准备阶段是进行内存分配。为类变量也就是类中由static修饰的变量分配内存,并且设置初始值,这里要注意,初始值是默认初始值0、null、0.0、false等,而不是代码中设置的具体值,代码中设置的值是在初始化阶段完成的。另外这里也不包含用final修饰的静态变量,因为final在编译的时候就会分配了。这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例对象会随着对象一起分配到Java堆中。

public class HelloApp {    private static int a = 1; // 准备阶段为0,而不是1    public static void main(String[] args) {        System.out.println(a);    }}

解析(Resolve)

解析主要是解析字段、接口、方法。主要是将常量池中的符号引用替换为直接引用的过程。直接引用就是直接指向目标的指针、相对偏移量等。


类的加载过程三:初始化(initialization)

  1. 初始化阶段就是执行类构造器方法()的过程
  2. 此方法不需要定义,是javac编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并而来
  3. 构造器方法中指令按语句在源文件中出现的顺序执行。
  4. ()不同于类的构造器(构造器是虚拟机视角下的())
  5. 若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕
  6. 虚拟机必须保证一个类的()方法在多线程下被同步加锁

需要注意,如果没有定义静态变量或静态代码块的话则没有()

案例如下:

public class HelloApp {    static {        code = 20;    }    private static int code = 10;    //第一步:在准备阶段初始化了code默认值为0。    //第二步:根据类执行顺序先执行静态代码块,赋值为20.    //第三步:最后赋值为10,输出结果为10.    public static void main(String[] args) {        System.out.println(code); // 10    }}

通过字节码文件可以很清楚的看到结果:

 0 bipush 20 2 putstatic #3  5 bipush 10 7 putstatic #3 10 return

先被赋值为20,然后改为10。


类加载器概述

JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader) 和 自定义类加载器(User-Defined ClassLoader)

从概念上讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器

无论怎么划分,在程序中最常见的类加载器始终只有三个:

系统类加载器(System Class Loader) < 扩展类加载器(Extension Class Loader) < 引导类加载器(Bootstrap Class Loader)

它们之间的关系不是继承关系,而是level关系。

0f96c9481bef98dd4d429001004cb0e3.png

系统类加载器和扩展类加载器间接或直接继承ClassLoader。划线分为两大类。

6490053fabff532a478d5e95ae81b33c.png

public class HelloApp {    public static void main(String[] args) {        //获取系统类加载器        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2        //获取其上层:扩展类加载器        ClassLoader extClassLoader = systemClassLoader.getParent();        System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@60e53b93        //获取其上层:获取不到引导类加载器        ClassLoader bootStrapLoader = extClassLoader.getParent();        System.out.println(bootStrapLoader);//null        //我们自己定义的类是由什么类加载器加载的:使用系统类加载器        ClassLoader classLoader = HelloApp.class.getClassLoader();        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2        //看看String是由什么类加载器加载的:使用引导类加载器        ClassLoader classLoaderString = String.class.getClassLoader();        System.out.println(classLoaderString);//null    }}

引导类加载器(Bootstrap ClassLoader)

  1. 这个类加载使用c/c++语言实现,嵌套在JVM内部。
  2. 他用来加载Java的核心库,(JAVA_HOME/jre/lib/rt.jar、resources.jar、或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类。
  3. 并不继承自java.lang.ClassLoader ,没有父加载器。
  4. 加载扩展类和应用程序类加载器,并指定为他们的父类加载器。
  5. 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类。

扩展类加载器(Extension ClassLoader)

  1. Java语言编写,由sun.misc.Launcher$ExtClassLoader实现。
  2. 派生于ClassLoader类。
  3. 上一层类加载器为启动类加载器。
  4. 从java.ext.dirs系统属性所指定的目标中加载类库,或从JDK的安装目录jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的Jar放在此目录下,也会自动由扩展类加载器加载。

系统类加载器(System ClassLoader)

  1. Java语言编写,由sun.misc.Launcher$AppClassLoader实现。
  2. 派生于ClassLoader类。
  3. 上一层类加载器为扩展类加载器。
  4. 它负责加载环境变量classpath或系统属性 java.class.path指定下的类库。
  5. 该类加载是程序中默认的类加载器,一般来说,Java应用的类都有由它来完成加载。
  6. 通过ClassLoader.getSystemClassLoader()方法可以获取该类加载器。

为什么需要用户自定义类加载器?

  • 隔离加载类
  • 修改类加载的方式
  • 扩展加载源
  • 防止源码泄露

用户自定义类加载器实现步骤

  1. 通过集成抽象类java.lang.ClassLoader类的方式,实现自己的类加载器。
  2. 在JDK1.2之前,在自定义类加载器时,总会去继承ClassLoader类并重写loadClass()方法,从而实现自定义的类加载器,但是在JDK1.2之后不再建议用户去覆盖loadClass()方法,而是建议把自定义类加载逻辑写在findClass()方法中。
  3. 在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自己编写findClass()方法其获取字节码流的方式,使得自定义加载器编写更为简洁。

关于ClassLoader

ClassLoader是一个抽象类,系统类加载器和扩展类加载器间接或直接继承ClassLoader。

常用方法如下:

0e850d9703a7e32f39d3ca2686c1b1c1.png


双亲委派机制

Java虚拟机对class文件采用的是按需加载的方式,也就是说需要使用该类的时候才会将它的class文件加载到内存生成class对象。

当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

工作原理

9e1acbd1e803e86793af583fcc7b64f9.png

  1. 如果一个类加载器收到了类加载请求,他并不会自己先去加载,而是把这个请求向上委托给上一级类加载器去执行。
  2. 如果上一级类加载器还存在上一级,则进一步向上委托,依次递归,请求最终会达到引导类加载器。
  3. 如果引导类加载器可以完成类加载任务,就成功返回。如果无法完成类加载任务,会依次往下再去执行加载任务。这就是双亲委派机制。

比如我们现在在自己项目中创建一个包名java.lang下创建一个String类。

package java.lang;public class String {    static {        System.out.println("我是自己创建的String");    }}
public class HelloApp {    public static void main(String[] args) {        String s = new String();    }}

执行之后并不会输出我们的语句,因为我们的String类加载器一开始由系统类加载器一级一级往上委托,最终交给引导类加载器,引导类加载器一看是java.lang包下的,ok,我来执行,最终执行的并不是我们自己创建String类,保证了核心api无法被纂改,避免类的重复加载。

package java.lang;public class String {    static {        System.out.println("我是自己创建的String");    }    public static void main(String[] args) {        System.out.println("Hello World !!!");    }}

如果我们想运行如上代码,我们会得到如下错误:

错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:   public static void main(String[] args)否则 JavaFX 应用程序类必须扩展javafx.application.Application

因为我们知道在核心api String类中是没有main方法的,所以我们可以确定加载的并不是我们自己创建的String类。

在JVM中表示两个Class对象是否为同一个类存在的必要条件:

  • 类的完整类名必须一致,包括包名。
  • 加载这个类的ClassLoader也必须相同。

顺便说一句,我们包名如果为java.lang则会报错。


- END -

6b6abee9d2df0946218b76b1d97c5622.png

3e169ea37041bd9ca252a627e29a4eb2.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/370566.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高可用集群搭建

高可用集群搭建  创建hadoop账户 创建hadoop账户&#xff08;#注意&#xff0c;接下来的操作均在hadoop账户下运行&#xff09; # useradd hadoop # passwd hadoopsu - hadoopmkdir soft disk1 disk2mkdir -p disk{1,2}/dfs/{dn,nn}mkdir -p disk{1,2}/nodemgr/local 将本地目…

scrt如何切换成英文版_英文版SecureCRT显示乱码解决

英文版SecureCRT显示乱码解决系统环境&#xff1a;CentOS&#xff15;.&#xff16;以前Linux都是默认安装在英文环境下&#xff0c;用英文版的SecureCRT查看系统内容输出的也都是英文的&#xff0c;不会出现乱码问题。今天同事在服务器安装时默认选择了简体中文&#xff0c;这…

java try catch_Java捕获异常

大家好&#xff0c;欢迎来到乐字节小乐的Java技术分享园地在Java中&#xff0c;凡是可能抛出异常的语句&#xff0c;都可以用try ... catch捕获。把可能发生异常的语句放在try { ... }中&#xff0c;然后使用catch捕获对应的Exception及其子类。多catch语句可以使用多个catch语…

多语言持久性:带有MongoDB和Derby的EclipseLink

从现在开始&#xff0c;多语种持久性一直是新闻。 从2011年底开始&#xff0c;在著名的Fowler帖子的推动下&#xff0c;我看到了更多更好的主意。 最新的一个是公司内部的学生项目&#xff0c;我们在其中使用Scala作为后端数据&#xff0c;将数据持久存储到MongoDB&#xff0c;…

web前端开发最佳实践--(笔记之JavaScript最佳实践)

如何避免全局变量污染&#xff1f; 避免定义全局变量或全局函数用一个变量进行封装&#xff0c;并返回外部需要访问的接口如何写出高维护的js代码 配置数据和代码逻辑分离 如&#xff1a; 改成&#xff1a; ---用js模板mustachehandlebarsjsMVC的数据模式 model&#xff1a;数据…

Python入门笔记

Python变量和数据类型 数据类型 print语句 注释 Python的注释以 # 开头&#xff0c;后面的文字直到行尾都算注释 # 这一行全部都是注释... print hello # 这也是注释 什么是变量 定义字符串 字符串可以用或者""括起来表示。 如果字符串本身包含怎么办&#xff1f;比如…

如何保证input的输入值不会随着提交 而变空_如何对web界面的应用进行测试?

一、输入框&#xff1a;1、字符型输入框&#xff1a;&#xff08;1&#xff09;字符型输入框&#xff1a;英文全角、英文半角、数字、空或者空格、特殊字符“~&#xff01;#&#xffe5;%……&*&#xff1f;[]{}”特别要注意单引号和&符号。禁止直接输入特殊字符时&…

CentOS6.x下,tomcat - web项目部署

1. 安装tomcat tomcat安装方法&#xff1a;http://www.cnblogs.com/vurtne-lu/p/6478440.html 2. 配置tomcat 修改server.xml文件 <!-- 使用 80 端口 (也可以使用其它端口)--> <Connector port"80" protocol"HTTP/1.1"connectionTimeout"200…

使用地图触发功能处理相干事件

本文介绍如何通过使用映射触发器来处理一致性事件。 基本上&#xff0c;建议使用Oracle Coherence中的分布式数据管理来研究Oracle Coherence API的基本配置和实现。 映射触发器是Oracle Coherence提供最高度定制的缓存管理系统的最重要功能之一。 MapTrigger代表一个功能代理…

阿里云服务器mysql莫名丢失_mysql数据库丢失

mysql数据库丢失云服务器(Elastic Compute Service&#xff0c;简称ECS)是阿里云提供的性能卓越、稳定可靠、弹性扩展的IaaS(Infrastructure as a Service)级别云计算服务。云服务器ECS免去了您采购IT硬件的前期准备&#xff0c;让您像使用水、电、天然气等公共资源一样便捷、高…

gitlab使用_使用 Docker 部署 Gitlab

GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的web服务&#xff0c;具有wiki和issue跟踪功能。GitLab是当前应用非常广泛的源代码管理系统。1. 安装docker引擎并启动2. 获取gitlab镜像包查看下载好的镜像3. 在…

druid连接池初始化慢_7、SpringBoot -连接池(Durid)

一导入相关核心包<dependencies>二 在application.ymlspring三、配置Druid Datasource(可选)Configuration五、监控访问 http://localhost:8080/druid&#xff0c; 使用上面配置的账号密码。四、自动配置原理源代码Configuration说明DataSourceProperties 配置相关 首先找…

使用工厂方法模式设计最佳实践

在前面的“设计模式”示例中&#xff0c;我们解释了当今常用的“工厂”模式。 在本节中&#xff0c;我们将了解具有更多抽象的更高级的解决方案。 该模式称为工厂方法设计模式。 定义&#xff1a; Factory方法模式提供了一种用于创建对象的方法&#xff0c;但是将对象创建委托…

qt绘制一圈圆_Qt绘制圆

最近开始折腾Qt了&#xff0c;手头上的一个项目需要用到Qt来绘制一些简单图像。记录下Qt绘制圆的过程&#xff1a;对于以A为圆心&#xff0c;半径为R的圆&#xff0c;外部有一个外切的正方形&#xff0c;正方形上有B点。如下图所示&#xff1a;对于void QPainter::drawArc(int …

前端基础之HTML

HTML介绍 Web服务本质 import socketsk socket.socket()sk.bind(("127.0.0.1", 8080)) sk.listen(5)while True:conn, addr sk.accept()data conn.recv(8096)conn.send(b"HTTP/1.1 200 OK\r\n\r\n")conn.secd(b"<h1>Hello world!</h1&g…

指令引用了 内存 该内存不能为read 一直弹窗_【翻译】使用Rust测试ARM和X86内存模型

原文标题: The Story of Tail Call Optimizations in Rust 原文标题: Examining ARM vs X86 Memory Models with Rust原文链接: https://www.nickwilcox.com/blog/arm_vs_x86_memory_model/公众号&#xff1a; Rust碎碎念苹果公司最近宣布&#xff0c;他们将要把笔记本和桌面电…

npm升级依赖包_Taro跨端开发之依赖管理

昨天跑的好好项目,今天跑不起来我们在开发周期比较长的前端项目的时候,必然会遇到依赖管理的问题. 我们在开发项目的时候,我们用了大量的三方库.这些三方的依赖库时不时的会更新自己的代码.第三方依赖库的代码更新会很容易造成代码运行的不稳定, 比如昨天还跑的好好的项目,另一…

设计模式 建造者模式 与 Spring Bean建造者 BeanDefinitionBuilder 源码与应用

建造者模式 定义: 将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示主要作用: 在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象如何使用: 用户只需要给出指定复杂对象的类型和内容, 建造者模式负责按顺序创建复杂对象…

java 布隆过滤器_什么是布隆过滤器(Bloom Filter)?

在日常工作中&#xff0c;有一个比较常见的需求&#xff0c;就是需要判断一个元素是否在集合中。例如以下场景&#xff1a;给定一个IP黑名单库&#xff0c;检查指定IP是否在黑名单中&#xff1f;在接收邮件的时候&#xff0c;判断一个邮箱地址是否为垃圾邮件&#xff1f;在文字…

STM32上使用JSON

一、STM32工程中添加JSON 最近在一网2串项目&#xff0c;串口和网口之间可能需要定义一下简单的通信协议&#xff0c;而通信协议上则需要去定义一下通信的数据格式&#xff0c;上次听剑锋说要用Json来定义&#xff0c;目前查了下资料具体如何去应用还不 会。因为最新的KEIL上支…