`
Action-人生
  • 浏览: 104208 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

java编译和类加载详述

    博客分类:
  • JAVA
阅读更多
来源于https://blog.csdn.net/HelloJave/article/details/83145719
java编译和类加载详述
Java程序运行时,必须经过编译和运行两个步骤。首先将后缀名为.java的源文件进行编译,最终生成后缀名为.class的字节码文件。然后Java虚拟机将编译好的字节码文件加载到内存(这个过程被称为类加载,是由加载器完成的),然后虚拟机针对加载到内存的java类进行解释执行,显示结果。

 

Java的运行原理
在Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中,这种供虚拟机理解的代码叫做字节码(ByteCode),它不面向任何特定的处理器,只面向虚拟机。每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。


 

Java代码编译整个过程
Java代码编译是由Java源码编译器来完成,流程图如下所示:








词法分析器:

词法分析器一般以函数的形式存在,供语法分析器调用。

这里的单词是一个字符串,是构成源代码的最小单位。从输入字符流中生成单词的过程叫作单词化(Tokenization),在这个过程中,词法分析器还会对单词进行分类。

词法分析器通常不会关心单词之间的关系(属于语法分析的范畴),举例来说:词法分析器能够将括号识别为单词,但并不保证括号是否匹配。

词法分析(lexical analysis)或扫描(scanning)是编译器的第一个步骤。词法分析器读入组成源程序的字符流,并且将它们组织成有意义的词素(lexeme)的序列,并对每个词素产生词法单元(token)作为输出。

简单的来说,词法分析就是将源程序(可以认为是一个很长的字符串)读进来,并且“切”成小段(每一段就是一个词法单元 token),每个单元都是有具体的意义的,例如表示某个特定的关键词,或者代表一个数字。而这个词法单元在源程序中对应的文本,就叫做“词素”。词法分析注重的是每个单词是否合法,以及这个单词属于语言中的哪些部分

token流:词法分析器的结果,就是把程序的语句进行分词得到的的一个个“单词”!

语法分析器:

是对token流进行语法检查、并构建由输入的单词组成的数据结构(语法树/抽象语法树)。语法分析器通常使用一个独立的词法分析器从输入字符流中分离出一个个的“单词”;语法分析的上下文无关文法注重的是一个一个的推导式,是将词法分析中得到的单词按照语法规则进行组合 

语法树/抽象语法树:

是源代码的抽象语法结构的树状表现形式,这里特指java的源代码。树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if(;;){ //当符合条件执行的任务}或者 while(true){//当符合条件执行的任务}这样的条件跳转语句,可以使用带有两个分支的节点来表示。

语义分析器:语义分析就是要了解各个推导式之间的关系是否合法,主要体现在推导式中使用的终结符和非终结符之间的关系,也就是它们的类型。

注解抽象语法树:经过 语义分析器将语法树/抽象语法树转化为注解抽象语法树

 

字节码生成:

目的:将注解语法树转化成字节码,并将字节码写入*.class文件。

流程:

将java的代码块转化为符合JVM语法的命令形式,这就是字节码
按照JVM的文件组织格式将字节码输出到*.class文件中
 

类加载详解:
在Java 中分为主动引用和被动引用 主动引用都会触发类的加载!比如:访问这个类的静态变量,方法,和 通过new ,jvm标记加载的类(存在main方法的类),反射等,父类在子类加载的时候也会被加载

被动引用:比如访问静态常量或者创建数组内部对象

类加载主要是由jvm虚拟机负责的,过程非常复杂,类加载分三步  加载   》  连接  》初始化

加载
程序运行之前jvm会把编译完成的.class二进制文件加载到内存,供程序使用,用到的就是类加载器classLoader ,java程序的运行并不是直接依   靠底层的操作系统,而是基于jvm虚拟机。

类加载器:负责读取字节码,并转换成java.Long.Class类的一个对象存在于方法区

加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始

注意:要判断两个类是否“相同”,前提是这两个类必须被同一个类加载器加载,否则这个两个类不“相同”。
这里指的“相同”,包括类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法、instanceof关键字等判断出来的结果。

java中加载器的种类大致可以分为四种:

1.Bootstrap ClassLoader(由C++语言写成)顶级加载器,%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。

2.系统加载器(也就是内部类AppClassLoader)   加载classpath下的路径下的class

3.ExtClassLoader,加载jre\lib\ext\classes文件下的class

4.java.net.UrlClassLoader. 加载指定的url下的class


当我们运行一个程序时,首先是找到JDK安装目下的jvm.dll来启动JAVA虚拟机。

而后Bootstrap ClassLoader产生。

接下来就是Bootstrap ClassLoader来加载ExtClassLoader,并且指定ExtClassLoader的父加载器为Bootstrap ClassLoader,但是因为Bootstrap ClassLoader用C++语言写的,所以用JAVA的观点来看,这个加载器的实例是不存在的所以ExtClassLoader的父加载器被设置为了null。

然后就是Bootstrap ClassLoader将AppClassLoader装载,并指定其父加载器为ExtClassLoader。

双亲委派模型:是通过组合实现的!并不是继承关系!


JAVA是按照加载器的委派模型来实现的。这种模型是JAVA安全性机制的保证。

如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都不愿意干活,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己想办法去完成,这不就是传说中的双亲委派模式。

 采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,可以防止核心API库被随意篡改

 

 

连接:
       连接是很重要的一步,过程比较复杂,分为三步  验证  》准备  》解析    

  验证:确保类加载的正确性。一般情况由javac编译的class文件是不会有问题的,但是可能有人的class文件是自己通过其他方式编译出来的,这就很有可能不符合jvm的编译规则,这一步就是要过滤掉这部分不合法文件 

  准备:为类的静态变量分配内存,将其初始化为默认值 。我们都知道静态变量是可以不用我们手动赋值的,它自然会有一个初始值 比如int 类型的初始值就是0 ;boolean类型初始值为false,引用类型的初始值为null 。 这里注意,只是为静态变量分配内存,此时是没有对象实例的 

  解析:将class 内的 符号引用,加载到 运行时常量池 内转化成为 直接引用 的过程解释一下符号引用和直接引用。比如在方法A中使用方法B,A(){B();},这里的B()就是符号引用,它对于方法的调用没有太多的实际意义。但是B方法实际调用时是通过一个指针指向B方法的内存地址,这个指针才是真正负责方法调用,他就是直接引用。

初始化:
为类的静态变量赋予正确的初始值,上述的准备阶段为静态变量赋予的是虚拟机默认的初始值,此处赋予的才是程序编写者为变量分配的真正的初始值

类加载完成!!!

备注:

当 JVM 遇到 new 指令时,首先去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载过,如果没有就先执行类加载。如果类已经被加载过,则会为新生对象分配内存(所需内存大小在类加载后就可以确定),分配对象内存采取的方式是“指针碰撞”或“空闲列表”,前者是在内存比较规整的情况下,后者是在空闲内存和已使用内存相互交错的情况下,而内存是否规整这又取决于垃圾回收器。

问:我们通过 Java 栈中对象的引用去访问这个对象,访问对象的主流方式有 那些

答 :使用句柄和直接指针。

使用句柄访问:在 Java 堆中会划分出一块内存作为句柄池,引用中储存的内容就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。


直接指针访问:在对象的内存布局中就要放置访问类型数据的指针。





这两种方式各有优势,使用句柄的好处是引用中存储的是稳定的句柄,对象被移动时(垃圾回收时对象被移动)只需改变句柄中的实例数据的指针,不需要改动引用本身。而使用直接指针的好处是速度更快,它节省了一次指针定位的开销。HotSpot 使用的是第二种方式进行对象的访问。

问:怎解决内存分配的线程安全问题?

答:jvm提供了2种解决内存分配的线程安全问题,1.使用cas无锁机制失败重试来保证操作的原子性 2.jvm 会给每条线程分配本地线程分配缓冲TLAB 可以通过配置来决定分配的大小,只有当分配内存用完后才会去锁进行同步操作

 
---------------------
作者:HelloJave
来源:CSDN
原文:https://blog.csdn.net/HelloJave/article/details/83145719
版权声明:本文为博主原创文章,转载请附上博文链接!
  • 大小: 40.1 KB
  • 大小: 32.7 KB
  • 大小: 15.3 KB
  • 大小: 229.5 KB
分享到:
评论

相关推荐

    java反编译工具(内附安装流程)

    JAD支持反编译Java 5到Java 8的类文件,并且提供了GUI界面和命令行接口,适用于不同需求的用户。 接下来,我们将详述JAD的安装步骤: 1. **下载JAD**: 首先,你需要从JAD的官方网站或可靠的第三方源获取最新版本的...

    Java虚拟机规范.Java SE 8版.zip

    第5章定义Java虚拟机启动以及类与接口的加载、链接和初始化过程;第6章阐释并列举Java虚拟机指令集;第7章提供一张以操作码值为索引的Java虚拟机操作码助记符表。 《Java核心技术系列:Java虚拟机规范(Java SE 8版...

    Java虚拟机规范.Java SE 8版

    《Java核心技术系列:Java虚拟机规范(Java SE 8版)...第5章定义Java虚拟机启动以及类与接口的加载、链接和初始化过程;第6章阐释并列举Java虚拟机指令集;第7章提供一张以操作码值为索引的Java虚拟机操作码助记符表。

    java虚拟机规范 jdk8.

     定义Java虚拟机启动以及类和接口的加载、链接和初始化过程。  阐释并列举Java虚拟机指令集。 【电子版来自互联网,仅供预览及学习交流使用,不可用于商业用途,如有版权问题,请联系删除,支持正版,喜欢的请购买...

    Java虚拟机规范(Java SE 7)中文版

    《Java虚拟机规范(Java SE 7版)》是Java领域最...第5章定义了Java虚拟机启动以及类和接口的加载、链接和初始化过程。第6章阐释并列举了Java虚拟机指令集。第7章提供了一张以操作码值为索引的Java虚拟机操作码助记符表。

    Java反射机制-PDF文档,详述了反射机制的原理和使用方法

    Java反射机制是Java编程语言中的一个强大特性,它允许程序在运行时检查和操作类、接口、对象等的内部结构。这一机制的核心在于Java.lang.reflect包,提供了Class、Constructor、Method和Field等类,用于获取类的信息...

    Java语言规范和JVM虚拟机规范.zip

    1. **类加载子系统**:描述了如何加载、验证、准备、解析和初始化类。 2. **运行时数据区**:包括堆、方法区、栈、本地方法栈和程序计数器,它们分别存储不同类型的运行时数据。 3. **字节码执行引擎**:解释执行...

    不错的反编译工具jd-gui-0.2.8.windows

    3. 通过菜单或拖放方式加载要反编译的Java类文件或整个.jar文件。 4. 在反编译视图中查看源代码,进行搜索或调试。 需要注意的是,由于反编译过程中可能出现的精度损失,反编译的源代码可能与原始源代码不完全相同...

    计算机JAVA语言入门

    ### 计算机JAVA语言入门知识点详述 #### 一、JAVA语言的起源与特性 **1.1 Java的历史背景** - **1.1.1 Java的起源** - Java最初由Sun Microsystems的一位名为James Gosling的工程师领导开发,项目代号为“Green...

    Java虚拟机规范 Java SE 8版

    第5章定义Java虚拟机启动以及类与接口的加载、链接和初始化过程;第6章阐释并列举Java虚拟机指令集;第7章提供一张以操作码值为索引的Java虚拟机操作码助记符表。 《Java核心技术系列:Java虚拟机规范(Java SE 8版...

    Java程序设计 PPT

    9. **Java反射API**:探索运行时动态加载类、获取类信息和创建对象的能力,以及其在元数据处理和插件系统中的应用。 10. **Java集合框架的高级话题**:包括并发容器如ConcurrentHashMap,以及Stream API的使用。 ...

    java基础知识总结文档

    Java 基础知识文档详述了Java编程语言的核心概念,包括其历史、技术架构、开发环境、命令行工具以及语法基础。以下是这些知识点的详细解释: 1. **Java概述**: Java最初由Sun公司的James Gosling等人开发,原名为...

    Java面试题大全_300个以上面试题加答案_最后附加例子

    - 阐述类加载机制,包括双亲委派模型和自定义类加载器。 9. **设计模式**: - 解释常见的设计模式,如单例模式、工厂模式、观察者模式、装饰器模式等。 - 分析每种设计模式的应用场景和优缺点。 10. **Java新...

    华为java培训讲义.doc

    此外,JVM在加载类文件时会进行字节码验证,确保代码的合法性和安全性。例如,检查操作是否合规,类型匹配,防止非法访问和越界操作等。 在Java源代码编写时,有几点需要注意:1) 文件名应与public类同名,扩展名为...

    Java课件.rar

    5. **集合框架**:详述Java集合框架,包括List、Set、Map接口,以及ArrayList、LinkedList、HashSet、HashMap等常见实现类的用法和特点。 6. **输入/输出流**:讲解Java的I/O流系统,如文件流、字符流、缓冲流、...

    Java语言程序设计

    8. **泛型**:泛型是Java 5引入的新特性,用于提高代码的类型安全性和可读性,可以在编译时检查类型,避免了强制类型转换。 9. **JVM**:Java虚拟机是Java程序的运行环境,负责加载、验证、执行字节码,实现跨平台...

Global site tag (gtag.js) - Google Analytics