Java虚拟机

一、 JVM的基础知识介绍

1.JVM的概念

JVM,即Java virtual Machine,Java 虚拟机。JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器。它是一种利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台,可以在上面执行java的字节码程序。
JVM有自己完善的硬件架构,如处理器、堆、栈、寄存器等,还具有相应的指令系统。Java语言最重要的特点就是跨平台运行(即一次编译,到处执行)。使用JVM就是为了支持与操作系统无关,实现蹄平台。

2.JRE/JDK/JVM

在这里插入图片描述

2.1JRE

JRE(JavaRuntimeEnvironment,Java运行环境),也就是Java平台。所有的Java程序都要在JRE下才能运行。普通用户只需要运行已开发好的java程序,安装JRE即可。

2.2JDK

JDK(Java Development Kit)是程序开发者用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也需要JRE才能运行。为了保持JDK的独立性和完婺性,在JDK的安装过程中,JRE也是安装的一部分。所以,在JDK的安装目录下有一个名为jre的目录,用于存放JRE文件。

2.3JVM

JVM(JavaVirtulMachine,Java虚拟机)是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

3、JVM生命周期

启动和消亡:
JVM负责运行一个Java程序。当启动一个Java程序时,一个虚拟机实例也就诞生了。当该程序关闭退出,这个虚拟机实例也就随之消亡。

3.1JVM运行起点

Java虚拟机实例通过调用某个初始类的main()方法来运行一个Java程序。而这个main()方法必须是共有的(public)、静态的(static)、返回值为void,并且接受一个字符串数组作为参数。任何拥有这样一个main()方法的都可以作为Java程序运行的起点。

3.2JVM两种线程;

守护线程非守护线程。守护线程通常是由虚拟机自己使用的,比如执行垃圾收集任务的线程。
但Java程序也可以把创建的线程标记为守护线程。而Java程序中的初始线程——main()的线程是非守护线程。
只要还有任何非守护线程在运行,那么这个Java程序也在继续运行。当该程序中所有的非守护线程都终止时,虚拟机实例将自动退出。
假若安全管理器允许,程序本身也能够通过调用Runtime类或者System类的exit()方法来退出。

二、JVM的工作过程

类加载的子系统:保证加载正确的类到JVM中
运行时数据区:类存储及对象运行时变量的存储区域
执行引擎:将java的字节码交小内行引擎,根据字节码逐个执行

2.1类加载的子系统

java的动态类的装载由装载子系统来实现的,进行加载,连接,在运行第一次引用的类时,进行初始化文件
加载功能是类的加载,共有三种类加载器:Bootstrap ClassLoader,Extension ClassLoader和Application ClassLoader
加载:查找并加戟指定的类的字节码文件连接:连接包含三块内容:验证.准备、解析
验证:验证加载的class的文件格式、元数据、字节码、符号引用验证
准备︰为类的静态的变量分配内存,并初始化默认值
解析阶段:把类中的符号引用转化为直接引用
初始化:为类的静态变量赋予正确的初始值并且执行静态代码块
执行引擎
执行存在的字节码信息
提供执行就是来解析字节码,优化字节码,生成中间代码,提供一些组件∶垃圾回收、和本地native交互的本地库等等…

2.2类加载的时机

主动初始化的方式
1、创建对象实例,new对象的时候,会对类进行初始化,前提是类没有被初始化
2、调用类的静态属性或者是类的静态方法
3、通过class文件反射创建对象
4、初始化一个类的子类,使用子类的时候先初始化父类
5、 java虚拟机启动时被标记为启动类的类,如: main方法所在的类
注: java类的加载是动态的,并不会一次性加载所有的类到JVM中才执行,保证程序能够正常运行的基础类,其他的类则在需要时才会加载,节约内存开销
不会进行分类加载的情况
1、在同一个类加载器下只能初始化类一次,已经初始化的就不会在进行初始化
2、在编译的时候就能确定下来的静态变量,不会对类进行初始化,比如final修饰的静态变量

2.2双亲委派模型

在这里插入图片描述
使用双亲委派模型的优点:
1、安全性,避免用户自己编写的类动态替换java的核心类
2、避免类的重复加载,因为JVM判定两个类是否是同一个类,不仅仅根据类名是否相同进行判定,还需要判断加载该类的类加载器是否是同一个类加载器,相同的class文件被不同的类加载器加载得到的结果就是两个不同的类。

三、JAVA内存模型

运行时数据区域(JMM)

  • 方法区:类级别数据。静态变量存储在这里。
  • 线程共享的堆区:对象及其实例变量和数据存储位置,线程共享
  • 虚拟机栈︰java程序执行过程中的调用关系,局部信息都会以栈帧形式存储在该位置,线程私有
  • 程序计数器:记录程序下一个执行的位置,线程私有
  • 本地方法栈:保存和OS交互的native方法信息。线程私有

5个区域:堆区(Heap)、方法区、虚拟机栈、本地方法栈、程序技术器
线程共享区域:方法区、堆区
线程私有区域:虚拟机栈、本地方法区、程序计数器
在这里插入图片描述

3.1程序计数器

特点:

  • 占用的JVM内存空间较小
  • 每个线程生命周期内独享自己的程序计数器(内部存放的是字节码指令的地址引用)
  • 不会发生OOM(内存溢出)
    程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
    作用:
    1、字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、异常处理。
    2、在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪个位置。
    注意:程序计数器是唯一一个不会出现OutOfMemoryError的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

3.2虚拟机栈

局部变量表
主要存放了编译器可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句或其他与此对象相关的位置)。
特点:

  • 内部结构是栈帧,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法返回地址等信息
  • 某方法在调用另一个方法是通过动态链接在常量池中查询方法的引用,进而完成方法调用。某方法在调用另一个方法的过程,即是一个栈帧在虚拟机中的入栈到出栈的过程
  • 虚拟机中的方法入栈的顺序和方法的调用顺序是一致的
    与程序计数器一样,Java虚拟机桂也是线程私有的,它的生命周期和线程相同,描述的是Java方法执行的内存模型。
    在这里插入图片描述
    Java虚拟机栈会出现两种常:
    StackOverFlowError和 `OutOfMemoryError。StackOverFlowError
    若Java虚拟机栈的内存大小不允许动态扩展,当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就抛出StackOverFlowError异常。
    OutOfMemoryError:
    若Java虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了,无法再动态扩展了,此时抛出OutOfMemoryError异常。

Java虚拟机栈也是线程私有的,每个线程都有各自的Java虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。

3.3本地方法栈

和虚拟机栈所发挥的作用非常相似,
区别是︰虚拟机栈为虚拟机执行Java方法〈也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native方法服务。
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。方法执行完毕后相应的栈帧也会出栈并释放内存空间,
也会出现 StackOverFlowError和OutOfMemoryError两种异常。

3.4堆

特点:

  • 存放Java对象和数组
  • 虚拟机中存储空间比较大的区域
  • 可能出现OutOfMemoryError异常区域
  • 该区域是GC 的主要区域,堆区由年轻代和老年代组成,年轻代又分为Eden区、So区(from survivor)、s1区(to survivor)、新生代对应Minor GC (Young GC) ,老年代对应Full GC (old GC)。

Java虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
Java 堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap) .从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代:
在细致一点有∶Eden空间、From Survivor、To Survivor空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

3.5方法区

方法与Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

HotSpot虚拟机中方法区也常被称为“永久代"”,本质上两者并不等价。仅仅是因为HotSpot 虚拟机设计团队用永久代来实现方法区而已,这样 HotSpot 虚拟机的垃圾收集器就可以像管理Java 堆一样管理这部分内存了。但是这并不是一个好主意,因为这样更容易遇到内存溢出问题。

相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就"永久存在”了。

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池信息(用于存放编译期生成的各种字面量和符号引用)
既然运行时常量池时方法区的一部分,自然受到方法区内存的限制,
当常量池无法再申请到内存时会抛出OutOfMemoryError异常。JDK1.7及之后版本的JVM已经将运行时常量池从方法区中移了出来,在Java 堆 (Heap)中开辟了一块区域存放运行时常量池。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值