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,一经查实,立即删除!

相关文章

JPA / Hibernate:基于版本的乐观并发控制

本文是Hibernate和JPA中基于版本的乐观并发控制的简介。 这个概念已经很老了&#xff0c;上面已经写了很多东西&#xff0c;但是无论如何我都看到了它被重新发明&#xff0c;误解和滥用。 我在编写它只是为了传播知识&#xff0c;并希望引起人们对并发控制和锁定的兴趣。 用例…

高可用集群搭建

高可用集群搭建  创建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语…

haproxy文件操作

import os #导入os模块def search(): #定义查找函数 with open(haproxy.txt,r) as f: #只读方式打开文件 value input(请输入您…

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

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

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

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

yum mysql5.7位置_CentOS yum 安装 Mysql5.7

1 Steps for a Fresh Installation of MySQL# wget https://dev.mysql.com/get/mysql57-community-release-el6-9.noarch.rpm# yum localinstall mysql57-community-release-el6-9.noarch.rpm以上步骤其实是把 MySQL Yum repository 添加到了系统的 repository list 里去了。ll…

HTML/CSS基础知识(四)

WEB标准和W3C的理解与认识 Web标准是一系列标准的集合。 网页主要由三部分组成&#xff1a;结构&#xff08;Structure&#xff09;、表现&#xff08;Presentation&#xff09;和行为&#xff08;Behavior&#xff09;。 对应的标准也分三方面&#xff1a;结构化标准语言主要包…

python做一个系统代码_python初学者,用python3实现基本的学生管理系统代码实例...

这篇文章分享了管理系统&#xff0c;python学生管理系统的使用&#xff0c;这篇文章非常详细地介绍了通过示例代码实现的学生管理系统&#xff0c;该系统对每个人的研究或工作都有一定的参考学习价值。 这个是用python实现的基本的增删改查的学生管理系统吧&#xff0c;其中主要…

Python入门笔记

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

1058. 选择题(20)

原题: https://www.patest.cn/contests/pat-b-practise/1058 思路: 本题主要就是怎么读取数据的问题, 一定要注意scanf函数匹配到 空格或者回车会结束当前变量的赋值, 并且会丢弃这个空格或回车. 关于如何判断一项答题是否正确, 可以采用循环一个一个判断, 也可拼成 字符串用st…

使用Spring和Hibernate进行集成测试有多酷

我有罪&#xff0c;直到现在才写集成测试&#xff08;至少针对数据库相关事务&#xff09;。 因此&#xff0c;为了消除内感&#xff0c;我阅读了如何在周末以最少的努力实现这一目标。 提供了一个小示例&#xff0c;描述了如何使用Spring和Hibernate轻松实现这一目标。 通过集…

假设mysql数据表t1有字段_使用ROMA Connect集成数据

概述ROMA Connect支持接入多种类型的数据源&#xff0c;并通过数据集成任务实现源端到目标端的数据集成转换。ROMA Connect支持相同结构数据之间进行集成转换&#xff0c;也支持异构数据之间进行集成转换。本章节通过完成一个SQL Server到MySQL的数据集成配置样例&#xff0c;帮…

vue-wechat-title

html中的title安装&#xff1a;npm install vue-wechat-title --save1.在mian.js中//网页titleimport VueTitle from vue-wechat-title Vue.use(VueTitle);2. 路由中加下 title { path: /, component: Index, meta: { title: 首页 } }3. 在app.vue 中修改 router-view &a…

如何保证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…

dedecms列表页面随机缩略图调用

如果要利用dedecms制作扁平化主题&#xff0c;大概也能够遇到相似的问题&#xff0c;那就是dedecms的缩略图机制&#xff0c;在没有缩略图的情况下显示单一的默认图片&#xff0c;如果是wordpress可以很方便的定义函数调用随机的缩略图&#xff0c;即便是在没有设置缩略图并且文…

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

本文介绍如何通过使用映射触发器来处理一致性事件。 基本上&#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;让您像使用水、电、天然气等公共资源一样便捷、高…