JVM

学习内容

JVM的基础知识
跨平台性、JVM概念、JDK/JVM/JRE、JVM的生命周期
JVM的工作过程
类装载子系统、运行时数据区域(JMM)、执行引擎
内存模型
JVM的参数配置
Java命令 java javac
JVM的调优
两个实际问题进行排查
JVM垃圾回收
垃圾回收算法(4种)、垃圾回收器(7种)

JVM的基础知识

Java的跨平台性

在这里插入图片描述
ava中提到跨平台,想到的是JVM,但是能跨平台的是Java程序,而不是JVM。JVM是用C/C++开发的,是编译后的机器码,是不能跨平台,不同的平台需要安装不同版本的JVM编写的Java文件,编译之后生成.class文件,称之为字节码文件,Java虚拟机(JVM)负责将字节码文件翻译乘法特定平台的机器码后才能运行。即只要在不同的平台上安装对应的JVM,然后运行字节码文件,即运行Java程序
在这个过程中,编写的Java程序没有改变,仅仅是通过JVM这个中间层,就能在不同的平台上运行。实现“一次编译,到处运行”的目的
如果是C你要在不同的平台上运行,得先在此平台环境上进行编译才能够运行

JVM的概念

JVM,即Java Virtual Machine,Java虚拟机
JVM是Java的核心和基础,在Java和OS平台之间的虚拟的处理器,他是利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台。在上面可以执行字节码程序
JVM有自己完善的硬件架构,如处理器,堆栈,寄存器,还有相应的指令系统,使用JVM就是为了支持与操作系统无关,实现跨品台。

JRE/JDK/JVM是关系

在这里插入图片描述
JRE(Java Runtime Environmnet,Java 运行环境),也叫做Java平台。**所有的Java程序都在在JRE下才能执行。对于普通的用户只需要运行已经开发好的Java程序,安装JRE即可。
JDK(Java Development Kit)是
程序开发者用来编译、调试Java程序用到的开发工具包,JDK的工具也是Java程序,也是需要JRE才能运行。**为了保证JDK的独立性和完整性,在JDK安装的过程中,JRE也是安装的一部分,所以,在JDK的安装目录下有一个jre的目录,存放的是JRE文件
JVM(Java Virtual Machine,Java的虚拟机),是JRE的一部分,是一个虚拟出来的计算机,通过在实际的计算机上模仿各种计算机的功能来实现的

在这里插入图片描述

JVM生命周期

JVM负责运行一个Java程序,当启动一个Java程序时,一个虚拟机实例也就产生了,当该程序关闭退出,这个虚拟机实例也就随之消亡了
JVM的运行起点:
JVM实例通过调用某个初始类的main方法来运行Java程序,而main()方法必须共有的(public)、静态的(static)、返回值为void星星,并且一个字符串数组作为参数,任何拥有这个main方法的都可以作为Java程序运行的起点
JVM中存在两种线程
守护线程和非守护线程

JVM的工作过程

在这里插入图片描述

基础介绍

JVM的架构图如上:将JVM划分为三个主要的子系统
类装载子系统(Class Loader Subsystem)
运行时数据区(Runtime Data Areas)
执行引擎(Execution Engine)

类装载子系统

Java的动态类装载功能是由类装载子系统实现,可以装载,链接,初始化文件
• 装载:功能是加载类,共有三种类加载器,Boot Strap Class Loader,Extension Class Loader和Application Class Loader
• 链接:
验证:字节码验证器将验证生成字节码是否正确,如果验证失败,将得到验证错误信息
准备:将所有的静态变量,内存被分配并分配初始值(并不是赋值) //static int a = 5 只会给a分配int类型的默认内存大小并不会赋5的值
解析:将所有的符合内存引用替换为来自方法区域的原始引用
• 初始化
静态变量都将被赋值,静态代码块会执行 //该处才会赋值5给a

运行时数据区域(JMM)

方法区:类级别的数据,静态变量存储位置(线程共享)
堆区:对象及实例变量和数据存储的位置(线程共享)
虚拟机栈:线程执行过程中的堆栈信息(线程私有)
本地方法栈:和JNI操作的堆栈信息(线程私有的)
程序计数器:执行程序过程记录当前指令的地址(线程私有)

执行引擎

将运行时数据区域内的字节码交由执行引擎来执行,执行引擎读取字节码并逐行来执行

类加载机制

Java的源代码经过编译生成相应的.class文件,也就是字节码文件,而字节码文件有通过JVM中解释器(执行引擎),编译成特定机器上的机器码
Java源文件-》编译-》字节码文件
字节码文件-》JVM(解释器)-》机器码

类加载时机

虚拟机规范中严格规定有且只有5种情况需要立即对类进行初始化(class加载到JVM中)
1.创建对象实例:new对象的实例,会对类做初始化,前提这个类没有被初始化
2.通过class文件反射创建对象
3.调用类的静态属性或为静态属性赋值或者是调用类的静态方法 (Person.talk())//前提Person类有被初始化过
4.初始化一个类的子类,使用子类的时候先初始化父类
5.Java虚拟机启动时被标记为启动类的类,比如main方法所在的类
注:Java类的加载是动态过程,并不是一次性将所有的类加载后在运行,而是要保证程序运行的基础类完全加载到JVM,至于其他的类,则在需要的时候才能进行加载,为了节省内存开销
不会进行初始化的情况:
1.在同一个类加载器下一个类只能被初始化一次,已经被初始化的类就不必要初始化了
2.在编译的时候能确定下来的静态变量,不会对类进行初始化,比如final修饰的静态变量

类加载器

负责将字节码文件加载到内存

类加载器介绍

在这里插入图片描述
各个加载器的责任:
Bootstrap ClassLoader(启动类加载器):负责加载 J A V A H O M E 中 / j r e / l i b / r t . j a r 里 的 所 有 问 c l a s s ( 里 面 由 C + + 实 现 ) E x t e n s i o n C l a s s L o a d e r ( 扩 展 类 加 载 器 ) : 负 责 加 载 J a v a 平 台 下 扩 展 功 能 的 一 些 J a r 包 。 包 括 JAVA_HOME中/jre/lib/rt.jar里的所有问class(里面由C++实现) Extension ClassLoader(扩展类加载器):负责加载Java平台下扩展功能的一些Jar包。包括 JAVAHOME/jre/lib/rt.jarclass(C++)ExtensionClassLoaderJavaJarJAVA_HOME/jre/lib/*.jar文件
APP ClassLoader(应用类加载器):负责加载classpath中指定的jar和目录中的class
User ClassLoader(自定义类加载器):

双亲委派模型

在这里插入图片描述
双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应该有自己的父类加载器

双亲委派模型的工作过程

1、当前的类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则返回原来已经加载的类
2、如果没有找到,就去委派父类加载器去加载。父类加载器也会采用同样的策略,查看自己已经加载的类中是否包含这个类。有则返回,没有就委托其父类去加载。直到委托到启动类加载器为止。应因如果父类加载器为空,代表使用启动类加载作为父类去加载该类
3、如果启动类加载器加载失败,就回会去使用扩展类加载器来尝试加载。继续失败则会使用APP ClassLoader来加载,如果存在自定义加载器就会到自定义加载器去尝试加载。继续失败就回去抛出一个异常:ClassNotFoundException.

使用双亲委派模型的好处

1、**安全性:避免用户自定义编写的类动态的替换Java的一些核心类。**如果不采用双亲委派模型的加载方式进行加载工作。用户随时使用自定义的类来动态的替换了Java核心API中的定义。
2、**避免类的重复加载。**JVM判定两个类是否是同一个类,不仅仅根据类名是否相同进行判定,还要根据类的加载器是否是同一个加载器,相同的class文件被不同的类加载得到的结果就是两个不同类

类加载的详细的过程

类被加载器加载到JVM中,有以下几个步骤:
加载:查找并加载类的字节码的文件数据,在堆中创建一个class类的对象
连接:连接又包含三部分:验证、准备、解析
验证:文件格式、元数据、字节码、符号引用验证
准备:为类的静态变量分配内存、并将其初始化为默认值
解析:将类中的符号引用转换为直接引用
初始化:为类的静态变量赋予正确的初始值
在这里插入图片描述
单例模式:在一个JVM实例中同一个class类只能存在一个对象实例
class User {
priavte static User user = null;
static {
user = new User();
}
public static User getSingle() {
return user;
}
}
//获取当前对象实例
User.getSingle();

Java的内存模型

JVM将内划分:
在这里插入图片描述
方法区(线程共享):常量、静态变量
堆内存(线程贡献):存放对象,垃圾回收的主要场地
程序计数器(线程私有):当前程序执行的字节码的位置指示器
Java虚拟栈(线程私有):保存局部变量,基本的数据类型以及堆内存中对象的引用变量
本地方法栈(线程私有):为JVM提供native方法的服务

程序计数器

程序计数器是一块较小内存空间,是**当前程序执行的字节码的行号指示器。**字节码解释器工作是通过改变这个计数器的值来选取下一条要执行的字节码指定。分支、循环、跳转、异常处理等功能都是要依赖这个技术器来完成,另外为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,个线程之间计数器是互不影响,独立存储的。计数器称为”线程私有“的内存
作用:
1、字节码解释器通过改变程序计数器来依次读取指定,实现代码的流程控制
2、在多线程情况下,程序计数器用于记录当前线程执行的位置,从而当线程切换回来的时候能够知道该线程上次执行到哪里了
异常:
程序计数器是唯一一个不会出现OutOfMemoryError的内存区域
生命周期:
随着线程的创建儿产生,随着线程的结束而死亡

虚拟机栈

与程序计数器一样,是线程私有的,他的生命周期和线程相同,描述的java方法的执行模型
Java的虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有的:局部变量表、操作数栈、动态链接、方法出口信息,每一个栈帧是一个方法
在这里插入图片描述
局部变量表:主要存放了编译器可知的各种数据类型(Boolean、byte、char、short…)、对应引用(reference类型,它不同于对象本身,是一个执行对象起始位置的引用指针,也可能是指向一个代表对象的句柄等 )
异常:
Java虚拟机栈会出现两种异常:StackOverFlowError(栈溢出异常)和OutOfMemoryError(内存溢出异常)
StackOverFlowError:
若虚拟机栈的内存大小不允许动态扩展,当线程请求栈的深度超过当前虚拟机栈的最大深度时候,就会抛出StackOverFlowError异常。
OutOfMemoryError:
若虚拟机栈的内存大小允许动态扩展。当线程请求栈内存用完是,无法动态扩展,此时抛出OutOfMemoryError异常。
生成周期:
Java虚拟机栈随着线程的创建而创建,随着线程的死亡而消亡

本地方法栈

和虚拟机栈发挥的作用是一样的
区别:
虚拟机栈为虚拟机执行Java方法(字节码)服务的,而本地方法栈则为虚拟机使用到Native方法服务
在本地方法执行的时候,本地方法也会粗黄健一个栈帧,用于存放本地方法的局部变量白哦,操作数栈,动态链接,出口信息,方法执行完毕后相应的栈帧的内存也会被释放掉
也会抛出StackOverFlowError和OutOfMemoryError

Java虚拟机中管理的内存最大的一块,堆是所有线程共享的一块内存区域,在虚拟机启动的时候创建,此内存区域是用来存放对象实例的。几乎所有的对象实例以及数组都是在这里分配
Java对堆是垃圾回收器主要管理的区域,垃圾回收的主要区域也是堆区,在现代的垃圾回收器采用的回收策略时,将堆分为:新生代、老年代、永久代
在这里插入图片描述
永久代说明:
JDK1.6及之前:常量池分配在永久代 String Str = “Hello”;
JDK 1.7:堆中有永久代,逐步“去永久代”
JDK1.8 及之后,无永久代, 移除了永久代,取而代之的是一个元空间(Metespace)的区域,(永久代是在JVM的堆空间中,而元空间使用的是物理内存,直接受到本机的物理内存限制)
会出现OutOfMemoryError的异常

方法区

方法区和Java堆一样的,是各个线程共享的内存区域,用于存放被虚拟机加载的类信息,常量,静态变量,即使编译器编译后的代码等数据,
类的版本、字段、方法、接口等描述信息以及常量池信息
异常:
当无法申请到内存时会抛出OutOfMemoryError异常

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值