java虚拟机(一)

本文深入介绍了Java虚拟机(JVM)的概念、生命周期、体系结构及内存管理。解释了JVM是如何加载类、执行代码以及管理内存的。此外还探讨了类加载机制、线程管理以及方法区、堆区和栈区的具体运作。

java虚拟机

JAVA虚拟机是什么?

1.抽象规范
2.一个具体实现
3.一个运行中的虚拟机实例

java虚拟机规范只是概念,具体的实现是来自于不同的提供商,并存于多个平台,用软件实现或者软件硬件结合实现。运行一个java程序同时,也就运行了一个java虚拟机实例。

java虚拟机的生命周期

运行一个java虚拟机,java程序启动,虚拟机启动。java程序关闭,虚拟机消亡。每个java程序都有单独的JAVA虚拟机实例。

java程序初始类中的main()方法,是程序初始线程的的起点,其他任何线程都是由初始线程启动的。

java虚拟机中有两种线程,守护线程和非守护线程;守护线程就是虚拟机自己使用(比如垃圾回收任务线程),当时也可以吧任何创建的线程标记为守护线程,初始线程是非守护线程

只要有非守护线程运行,java虚拟机仍然存活。

java虚拟机的体系结构

java虚拟机规范中:虚拟机实例行为按照子系统、内存区、数据类型、指令来描述。这几种组成一起展示了虚拟机内部抽象体系结构。规范只是定义外部特征。

这里写图片描述

类装载器子系统:根据给定的全限定名来装入类型(类或接口)
执行引擎:负责执行包含在被装载类中方法的指令
运行时数据区:存储字节码、从已装载的class文件中得到的其他信息、程序创建的对象、传递给方法的参数、返回值、局部变量、以及运算的中间结果等等。

1.运行时数据区是抽象概念,结构实现的细节由具体实现的设计者决定。使得java虚拟机可以很容易的在各种计算机上实现。

2.某些运行时数据区是由程序中所有线程共享,有些是独享。每个JAVA虚拟机实例都有一个方法区以及一个堆,他们是由该虚拟机实例所有线程共享。

3.java栈是由许多栈桢组成,一个栈桢包含一个java方法的调用状态。当一个线程调用一个java方法时,虚拟机压入一个行的栈帧到改栈中,当方法返回时,这个栈帧从java栈中弹出并抛弃。

4.java虚拟机没有寄存器,其指令集使用java栈来存储中间数据。

这里写图片描述

这里写图片描述

1.数据类型

这里写图片描述

1.用int或者byte来表示boolean。
2.boolean数组当做byte数组来访问。
3.内部使用的基本类型:returnAddress,程序员不能使用,是用来实现java程序中的finally子句。

2.字长

java虚拟机中最基本的数据单元就是字,虚拟机设计者决定大小,字长必须足够支持所有类型,两个字长可以支持long和double,至少需要32位

类装载器子系统

1. JVM三种预定义类型类加载器

1.启动(Bootstrap)类加载器:引导类装入器是用本地代码实现的类装入器,它负责将 /lib下面的核心类库或-Xbootclasspath选项指定的jar包加载到内存中

2.扩展(Extension)类加载器:扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量-Djava.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。

3.系统(System)类加载器:系统类加载器是由 Sun的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径java -classpath或-Djava.class.path变量所指的目录下的类库加载到内存中。开发者可以直接使用系统类加载器。

2.类加载双亲委派机制

JVM在加载类时默认采用的是双亲委派机制。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

这里写图片描述

系统类加载器的父类加载器是标准扩展类加载器,标准扩展类加载器的父类加载器是启动类加载器

注意:编写自定义类加载器时不建议用户覆写loadClass(…)方法,而是覆写findClass(…)逻辑。

“`
//用户自定义类加载器WrongClassLoader.Java(覆写loadClass逻辑)
public class WrongClassLoader extends ClassLoader {

public Class<?> loadClass(String name) throws ClassNotFoundException {  
    return this.findClass(name);  
}  

protected Class<?> findClass(String name) throws ClassNotFoundException {  
    // 假设此处只是到工程以外的特定目录D:\library下去加载类  
    // 具体实现代码省略  
}  

}

“`

方法区 堆 栈

堆区:所有对象的实例分配都在Java堆上分配内存,堆大小由-Xmx和-Xms来调节

1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

栈区:栈是存放线程调用方法时存储局部变量表,操作,方法出口等与方法执行相关的信息,栈大小由Xss来调节,方法调用层次太多会撑爆这个区域

1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

方法区:方法区是存放虚拟机加载类的相关信息,如类、静态变量和常量,大小由-XX:PermSize和-XX:MaxPermSize来调节,类太多有可能撑爆永久带

1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
3.存放了类而不是实例,虚拟机会在需要装载的时候,通过常量池中的引用读取方法区中的class类 然后在堆中开辟出实例额的空间。

public   class  AppMain                

//运行时, jvm 把appmain的信息都放入方法区

{

public   static   void  main(String[] args)  //main 方法本身放入方法区。

{

Sample test1 = new  Sample( " 测试1 " );   //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面

Sample test2 = new  Sample( " 测试2 " );

test1.printName();

test2.printName();

}

} 

堆区

1.堆空间存储者类的实例,不必是连续的内存空间
2.JAVA虚拟机没有规定JAVA对象在堆中如何表示,由JAVA虚拟机实现者决定
3.可能的两种设计:
一个句柄池,一个对象池,一个指向对象实例的指针,一个指向方法区类型数据的指针,有利于堆碎片的整理。
这里写图片描述

对象指针指向一组数据,而该数据包括对象实例数据以及指向方法区中类数据的指针。一个指针就可以访问对象,但是移动对象变得更复杂。
这里写图片描述

方法表:

不管虚拟机实现什么样的对象表示法,很可能每个对象都有一个方法表,因为加快了调用实例方法时的效率,但是JAVA虚拟机规范并未要求必须使用方法表,所以一些严格内存资源限制的代码中并不会使用
这里写图片描述

程序计数器

JAVA中的PC寄存器,大小是一个字长,能够持有一个本地指针,也能够持有一个returnAddress。PC寄存器中的内容总是吓一跳将被执行命令的”地址”。这里的地址可以是方法字节码中相对于该方法起始指令的偏移量。如果执行的是本地方法PC寄存器的值是”undefined”

java栈

启动新线程时,java虚拟机都会为他分配一个JAVA栈,栈以帧为单位保存线程的运行状态。虚拟机只会对java栈执行两种操作:已帧为单位压栈或出栈。

某个线程正在执行的方法被称为该线程的当前方法,当前方法使用的帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。

每当线程调用一个java方法视,虚拟机都会在该线程的java栈中压入一个新帧,这个新帧就会成为当前帧,当执行这个方法时,他使用这个帧来存储参数,局部变量,中间运算结果。

方法可以用两种方式完成。一种通过return返回的,一种通过抛出异常终止。都会将该当前帧弹出java栈释放掉。

java栈上的所有数据都是私有的

和方法区一样内存不必连续。

栈帧

局部变量区,操作数栈,帧数据区。

局部变量区

JAVA栈帧的局部变量区被组织为一个以字长为单位、从0开始计数的数组,字节码指令通过从0开始的索引来使用其中的数据。类型为int、float、reference和returnAddress的值在数组中只占据一项,而类型为byte、short和char的值在存入数组前都将被转化未int值。long和double的值占据连续的两项。

局部变量区包含对应方法的参数和局部变量

操作数栈

不是通过索引来访问,通过标准的栈操作 —压栈和出栈 来访问,如果某个指令八一个值压入栈操作数栈中,稍后另一个指令就可以弹出这个值来使用。

java虚拟机没有寄存器 JAVA中的指令是从栈操作数栈中回去操作数的。

帧数据区

支持常量池解析,正常返回以及异常的派发机制

这里写图片描述

本地方法栈

java线程调用本地方法时,会保持java栈不变,不再在线程的JAVA栈中压入新的帧,虚拟机只是简单的动态连接并直接调用指定的本地方法

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值