一、Java跨平台
Java是一种可以跨平台的编程语言,我们通常把CPU处理器与操作系统构成的计算机系统整体称为平台。
1、Java可以跨平台的原因
-
Java可以跨平台的最主要原因是因为
JVM
,因为不同的平台,使用的JVM
不一样。 -
我们编写的Java源代码在编译后会生成一个Class文件,称为字节码文件。Java虚拟机负责将字节码文件翻译成特定平台下的机器代码,然后运行。
二、Java代码执行流程
Java程序的执行过程简单来说包括:
-
Java源代码编译成字节码;
-
字节码校验并把Java程序通过类加载器加载到
JVM
内存中; -
在加载到内存后针对每个类创建Class对象;
-
字节码指令和数据初始化到内存中;
-
找到main方法,并创建栈帧;
-
初始化程序计数器内部的值为main方法的内存地址。
-
程序计数器不断递增,逐条执行Java字节码指令,把指令执行过程的数据存放到操作数栈中,执行完成后从操作数栈取出后放到局部变量中,遇到创建对象,则在堆内存中分配一段连续的空间存储对象,栈内存中的局部变量表存放指向堆内存的引用;遇到方法调用则在创建一个栈帧,覆盖到当前栈帧的上面。
三、jdk
和jre
的区别
-
JDK
是开发环境,JRE
是运行环境。 -
JDK
包含JRE
,JRE
包含JVM
。
四、Java类加载机制
类在运行期间第一次使用时,被类加载器动态加载至JVM
。JVM
不会一次性加载所有类。因为如果一次性加载,那么会占用很多的内存。
类的生命周期包括以下7个阶段:
-
加载(Loading)
-
验证(Verification)
-
准备(Preparation)
-
解析(Resolution)
-
初始化(Initialization)
-
使用(Using)
-
卸载(Unloading)
类加载过程:加载、验证、准备、解析、初始化。
-
加载:通过类的完全限定名获取定义该类的二进制字节流;将该字节流表示的静态存储结构转换为
Metaspace
元空间区的运行时存储结构;在内存中生成一个代表该类的Class对象,作为元空间区中该类各种数据的访问入口。 -
验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
-
准备:为类变量分配内存并设置初始值,使用的是元空间区的内存。
-
解析:将常量池的符号引用替换为直接引用的过程。
-
初始化: 执行类构造器<clinit>()方法的过程。在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其他资源。<clinit>()是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序由语句在源文件中出现的顺序决定。所以,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。
五、类加载的时机
-
主动引用
-
当遇到
new
、getstatic
、putstatic
、invokestatic
这四条字节码指令时。 -
反射调用,使用
Class.forname()
或者newInstance()
等,如果类没初始化,则触发类加载。 -
加载一个类,如果它的父类还未被加载,则先触发该父类的加载。
-
加载包含main方法的主类。
-
如果有被
default
关键字修饰的接口方法的实现类发生了加载,则该接口要在实现类之前被加载。
-
-
被动引用
除主动引用之外,所有引用类的方式都不会触发加载,称为被动引用。
-
通过子类引用父类的静态字段,不会导致子类被加载。
-
通过数组定义来引用类,不会触发此类的加载。(该过程会对数组类进行加载,数组类是一个由虚拟机自动生成的、直接继承自 Object 的子类,其中包含了数组的属性和方法。)
-
常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的加载。
-
五、类与类加载器
概述:两个类相等,需要类本身相等,包括类的Class对象的equals()方法、isAssignableFrom()
方法、isInstance()
方法的返回结果为true,也包括使用Instanceof
关键字做对象所属关系判定结果为true。
除此之外,还要求两个类使用同一个类加载器进行加载,因为每一个类加载器都拥有一个独立的类名称空间。
分类:启动类加载器、扩展类加载器、应用程序类加载器。
-
启动类加载器:加载核心类库。
-
扩展类加载器:加载扩展类库。
-
应用程序类加载器:加载自定义类或者第三方jar包。
六、对象的创建过程
-
类加载检查:虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已经被加载过、解析和初始话过。如果没有,那必须先执行相应的类加载过程。
-
分配内存:为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。内存分配的查找方式有”指针碰撞“和”空闲列表“两种。
-
初始化零值:内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值。
-
设置对象头:根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
-
执行
init
构造方法:把对象按照程序逻辑的意愿进行初始化,这样一个真正可用的对象才算完整创建出来。
七、一个对象包含的内容
对象头、示例数据、对齐填充。
八、对象头包含什么
Mark Word(用于存储对象自身的运行数据)、Klass Pointer
(对象指向它类型元数据的指针)、数组长度。