Java代码执行过程概述

Java代码经历三个阶段:源代码阶段(Source) -> 类加载阶段(ClassLoader) -> 运行时阶段(Runtime)



      

首先我们来理清一下Java代码整个执行过程, 让我们对其有个整体的认识:

Java源程序(.java)经过Java编译器(javac)以后, 生成一个或多个字节码(.class)文件, JVM将每一条要执行的字节码通过类加载器ClassLoader加载进内存, 再通过字节码校验器的校验, Java解释器翻译成对应的机器码,  最后在操作系统解释运行.

当程序要使用某个时, 如果该还未被加载到内存中, 则系统会通过加载, 连接, 初始化三步来实现对这个进行初始化:

加载就是将class文件读入内存, 并为之创建一个Class对象(任何被使用时系统都会创建且只创建一个Class对象)

  JVM进行类加载阶段需要完成以下三件事情:

    1. 通过一个的全限定名称来获取定义此类的二进制字节流

    2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

    3. 在java中生成一个代表这个类的java.lang.Class对象, 作为方法区这些数据的访问入口

  类的加载的最终产品是位于区中的Class对象, Class对象封装了类在方法区内的数据结构, 并且向Java程序员提供了访问方法区内的数据结构的接口

  加载时机

    1. 创建类的实例

    2. 使用类的静态变量或者为静态变量赋值

    3. 调用类的静态方法

    4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

    5. 初始化某个类的子类

    6. 直接使用java命令来运行某个主类

  通俗的说就是只要用到了类的东西类就会加载

  JVM在运行时会产生3个类加载器组成的初始化加载器层次结构

  • Bootstrap ClassLoader 根类加载器

   用C++编写

  也被称为引导类加载器, 负责java核心类的加载 该加载器无法直接获取

  比如System, String等, 在JDK中JRE的lib目录下rt,jar文件中

  • Extension ClassLoader 扩展类加载器

  负责JRE的扩展目录中jar包的加载 jre/lib/ext目录下的jar包或-Djava,ext,dirs指定目录下的jar包装入工作库

  • System ClassLoader 系统类加载器(加载自己写的类以及第三方类库(导入的jar包))

  负责在JVM启动时加载来自java命令的class文件, 以及classpath环境变量所指定的jar包和类路径

连接就是将类的二进制数据合并到JRE 

  连接分为以下三步:

    验证 检查载入Class文件数据的正确性

      • 文件格式检验:检验字节流是否符合Class文件格式的规范, 并且能被当前版本的虚拟机处理
      • 元数据检验:对字节码描述的信息进行语义分析, 以保证其描述的内容符合Java语言规范的要求
      • 字节码检验:通过数据流和控制流分析, 确定程序语义是合法、符合逻辑的
      • 符号引用检验:符号引用检验可以看作是对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验

      是否有正确的内部结构(构造器, 方法, 变量, 代码块), 并和其他类协调一致

    准备 该阶段正式为类变量分配内存并设置类变量初始值

      这些变量所使用的内存将在方法区中进行分配, 此时进行内存分配的仅包括类变量, 而不包括实例变量(实例变量将会在对象实例化时随着对象一起分配在Java堆中),

      另外, 在这里分配的静态类变量是将其值定义为默认值, 这里所设置的初始值通常情况下是数据类型默认的零值(如0, 0L, null, false等), 而不是被在Java代码中

      被显式地赋予的值, 正确的赋值将在初始化阶段执行,

    解析 将类的二进制数据中的符号引用替换为直接引用

      比如说类中方法中的运算, 运算中符号a=1 去掉a直接变成1, 这样可以节约很多资源

初始化就是对类的静态变量, 静态代码块执行初始化操作

类初始化阶段是类加载过程的最后一步, 前面的类加载过程中, 除了加载(Loading)阶段用户应用程序可以通过自定义类加载器参与之外, 其余动作完全由虚拟机主导和控制, 到了初始化阶段, 才真正开始执行类中定义的Java程序代码

  初始化为类的静态变量赋予正确的初始值, JVM负责对类进行初始化, 主要对类变量进行初始化, 在Java中对类变量进行初始值设定有两种方式:

    • 声明静态变量(类变量)时指定初始值
    • 使用静态代码块为类变量指定初始值

初始化步骤:

1. 假如这个类还没有被加载和连接, 则程序先加载并连接该类

2. 假如该类的直接父类还没有被初始化, 则先初始化其直接父类

3. 假如类中有初始化语句, 则系统依次执行这些初始化语句

  JVM在堆内存中创建对象, 类的成员变量进入到堆内存中, 赋默认值

最后就是我们熟悉的Runtime运行时阶段

Person p = new Person();p,study();

执行上述代码会在堆内存创建一个Person类的对象, 并且在栈内存分配一块储存空间存放Person类型的引用变量p, p存放该对象的地址并且指向该对象, 调用p的study方法实际是, 对象通过Person类的字节码对象来访问方法区Person字节码的study方法

 



名词解释 :

Java源程序: 即Java源代码, 用java语言编写的程序

Java类加载器(Java Classloader): 是Java运行时环境(JRE)的一部分, 负责动态加载Java类到JVM的内存空间中

JRE: Java Runtime Environment, Java运行环境,内部包含了一个Java虚拟机以及一些标准类库(Jar包)

JAR包: 通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等)文件到一个文件, 以便开发Java平台应用软件或库

JVM: Java Virtual Machine一种能够运行Java字节码(Java bytecode)的虚拟机

类(Class): 类是具有共同属性和行为的对象的集合, 类定义了对象的属性和方法

字节码: 字节码是已经经过编译, 但与特定机器码无关, 需要解释器转译后才能成为机器码的中间代码

Java字节码: 是Java虚拟机执行的一种指令格式

Java编译器: 将Java源文件(.java文件)编译成字节码文件(.class文件, 是特殊的二进制文件, 二进制字节码文件), 这种字节码就是JVM的“机器语言”, javac命令可以简单看成是Java编译器

Java解释器: 是JVM的一部分, Java解释器用来解释执行Java编译器编译后的程序, java命令可以简单看成是Java解释器

运行时类: 加载到内存中的字节码文件对应的类称为运行时类,  此运行时类即为一个Class的实例

Java堆(Heap): 在JVM启动时创建, JVM所管理的内存中最大的一块JVM 中,堆(Heap)是可供各条线程共享的运行时内存区域, 也是供所有类实例和数组对象分配内存的区域,  Java堆是被所有线程所共享的一块内存区域, Java堆是垃圾收集器管理的主要区域

Java栈(Stack): 在函数中定义的基本类型的变量、Java指令代码、对象的引用变量均在函数的栈内存中分配,当超过变量的作用域后,Java 会自动释放掉该变量分配的内存空间

方法区(Non-Heap):  方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息(构造方法和接口定义)、常量、静态变量、即时编译器编译后的代码等数据, 运行时常量池存在方法区中

原文地址:https://www.cnblogs.com/yangshaox/p/11611696.html

时间: 2024-10-07 15:08:01

Java代码执行过程概述的相关文章

Java异常处理机制就是程序代码执行过程

我也是通过各种方式在进行验证是否可以满足我们的需求,今天我就发现了一个问题.现在我们来一起说明一下,这个可能不算是bug,而应该需要我们记住就可以了. 对于一副灰度图像I,她的每一个像素点I(x,y)都有一个灰度值,一般情况下可能的灰度取值有2^8=256个(0,1,...,255).如果我们统计出灰度值r在I中出现的次数n,并对其进行归一化(n/N,N是所有灰度值出现次数的总和),这样我们就可以得到像素r在I中出现的概率p(r).如果对每一个可能的灰度取值r都做同样的处理,我们可以得到如图1左

第一章 Java代码执行流程

说明:本文主要参考自<分布式Java应用:基础与实践> 1.Java代码执行流程 第一步:*.java-->*.class(编译期) 第二步:从*.class文件将其中的内容加载到内存(类加载)(运行期) 第三步:执行代码(运行期) 2.代码编译 javac命令将源码文件编译为*.class文件. 后边将介绍: javac将*.java编译成*.class文件的过程 class文件的文件格式,以及其存储的内容 3.类加载 主要是指将*.class文件加载到JVM,并形成Class对象的机

java程序执行过程&amp;基本数据类型

1. 程序load到内存. 2. 找到程序入口方法(main())开始执行. 3. 程序在内存中的存放 3.1 代码段(code segment)--------存放代码 3.2 数据段(data segment)--------存放静态变量,字符串常量 3.3 栈(stack)                ----------存放局部变量 3.4 堆(heap)                -----------存放new出来的东西 ----------------------------

Javascript代码执行过程-《悟透Javascript》笔记

本文摘录自李战老师<悟透Javascript>一书的部分章节,为适应博客发表作了一点点修改. 1) 预编译分析. JavaScript执行引擎将所有定义式函数直接创建为作用域上的函数变量,并将其值初始化为定义的函数代码逻辑,也就是为其建立了可调用的函数变量. “var”定义的变量也会在这一步中创建起来,初始值为undefined. 2) 开始解释执行代码. JavaScript 执行引擎并非一行一行地分析和执行程序,而是一段一段地分析执行的.而且,在同一段程序的分析执行中,定义式的函数语句会被

Android JAVA代码执行shell命令

Android中级篇之用JAVA代码执行shell命令 [日期:2011-12-08] 来源:Linux社区  作者:y13872888163    在Android可能有的系统信息没有直接提供API接口来访问,为了获取系统信息时我们就要在用shell指令来获取信息,这时我们可以在代码中来执行命令 ,这里主要用到ProcessBuilder 这个类. 代码部分  : 1.package com.yin.system_analysis; 2.import java.io.File; 3.impor

08 java代码块的概述和分类

08.01_面向对象(代码块的概述和分类) A:代码块概述 在Java中,使用{}括起来的代码被称为代码块. B:代码块分类 根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解). 局部代码块:只要是和局部有关系的,都是和方法有关系的 局部变量:在方法声明上或者在方法内部 构造代码块与类变量谁在前先加载谁 ,所有静态的东西(静态方法和静态变量都是)只加载一次,就是在类文件加载的时候加载,类文件释放的时候释放,加载顺序为,静态,–>–>构造代码块或局部变量

通过Java代码执行shell命令/脚本

JDK自带的两种方式有通过Runtime.getRuntime().exec()和ProcessBuilder类来做, 后者是JDK1.5以后引入的,官方也建议放弃使用Runtime的方式来做.今天在实现的时候就是采用ProcessBuilder,apache commons类库也提供了一个exec包专门做这类功能,这次暂时没用到. 在编写过程中,遇到几个比较坑的地方: 1.构建ProcessBuilder采用的参数: 建议采用"/bin/bash". "-c",

Java程序执行过程

首先,写好Java代码,保存到硬盘中.然后在命令行中输入: javac ClassName.java 此时,这个Java类文件将编译成字节码(.class)文件.如果用Eclipse等IDE开发工具,则当你保存代码的时候,这些开发工具已经完成了上述的手动编译工作,所以可以在对应的目录看到class文件.此时的class文件依然保存在硬盘中保存,所以你要写: java ClassName //来执行这个Java类 现在,JRE将从硬盘中读取这个class文件,载入到系统分配给JVM的内存区域——运

java 代码执行cmd 返回值异常

java 代码中调用cmd 命令执行 mysql 脚本结果 cmd 命令返回结果为1的问题: Process process = Runtime.getRuntime().exec(cmd);int waitFor = process.waitFor(); 上边waitFor值为1,而值为0才是执行正常,那我怎么才能知道这个执行结果是因为什么出错的呢?可以把出错信息打印出来就行了,process对象 有一个异常流,打印一下就好了: FileInputStream errorStream = (F