JVM
一.概念
jvm:java虚拟机,用来执行class文件,保证java语言的跨平台性
jvm就是一个字节码翻译器 ,将字节码翻译成各个系统对应的机器码,保证这些字节码可以在不同平台中正确运行
java虚拟机:可以当做有个虚拟的计算机,拥有自己的指令集和各种运行内存
二.jvm内存结构图
结构图
1.类加载器子系统
程序首次运行:加载————>连接--------->初始化
1.加载:加载类文件,通过加载器(引导类加载器,扩展类加载器,应用程序加载器)加载
2.连接:连接操作,验证(验证字节码是否正确),准备(为静态变量分配内存空间和默认值),解析
3.初始化:初始化静态变量和静态代码块
2.运行时数据区
1.方法区:储存类级别的数据,方法区在jvm中只有一个是多线程所共享的
2.堆区:储存所有的对象,变量,数组,堆区在jvm中只有一个是多线程所共享的
3.虚拟机栈:线程私有的,每个线程都会创建单独的运行时栈,每个方法调用时会在运行时栈中创建一个栈 帧,局部变量储存在占内存中
4.程序计数器:线程安全的,每个线程都会创建单独的程序计数器,用于保存当前所要执行的指令号,一旦指 令执行,计数器会更新到下一条指令
5.本地方法区:线程安全的,每个线程都会创建独立的方法区,用于保存本地方法信息
3.执行引擎
1.解释器:读取字节码,并逐一执行
2.即时编译器:如果一段代码被多次重复调用,都需要解释执行,即时编译器会将这样的字节码编译为本地代 码,用于多次重复调用提高性能
3.本地方法接口:与本地方法库进行交互,提供执行引擎所需要的本地库
4.本地方法库:执行引擎所需要的本地库的集合
三.程序计数器
作用:保存当前要执行的指令地址(指令号),一旦指令执行,计数器会更新到下一条指令的地址(指 令号)
程序计数器是线程安全的(运行数据区中唯一一个不会发生内存泄露的区域)
jvm指令通过 javap -v test.class反解析获得反解析工具
作用:根据class字节码文件,反解析当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏 移量映射表、常量池等信息
执行过程:
1.计数器读入 要执行的jvm指令号
2.CPU获得计数器中的指令号
3.CPU根据计数器中的指令号执行相对应的指令
4.计算器更新要执行的jvm指令
四.虚拟机栈(栈)
虚拟机栈就是java方法执行的内存模型
每个执行的线程都有自己的虚拟机栈(系统为当前线程分配的内存空间),是通过方法调用来实现的
在虚拟机栈中,每个方法都对应了一个栈帧
每个线程只能有一个活动栈帧,对应着当前正在执行的方法,该栈帧处于栈顶
虚拟机栈会发生内存溢出
每个栈帧中包含:局部变量表、操作数栈、动态链接、方法返回值等信息
五.栈帧
1.结构
每个栈帧都有独立的储存空间
2.组成
1.局部变量表:
储存方法中的局部变量(包括在方法中声明的非静态变量及函数形参)
储存:对于基本类型的变量,直接存储它的值(long和double占用两个局部变量,其他类型占一个)
对于引用类型的变量,储存的是指向对象的引用
局部变量表的大小在编译期就可以知道大小,因此在执行期间局部变量表的大小不会改变
2.操作数栈:
当一个方法开始执行时,操作数栈为空
随着方法的执行,会从局部变量表或对象实例的字段中复制所需要的数据写入到操作数栈,在随着计 算的进行 将栈中的元素出栈 到局部变量表 或者返回给调用者
3.动态链表:
指向运行时常量池的引用
在 class 文件中,描述一个方法调用或访问其成员变量时通过符号引用来表示的,动态
链接的作用就是将这些符号引用所表示的方法转换为实际方法的直接引用
4.方法返回地址:
方法调用的返回,包含正常返回(有返回值)和异常返回(无返回值),不 同的返回值类型有不同的指令
无论方法采用何种方式退出,在方法退出后都需要返回到方法调用的位置,程序才能继
-续执行,方法返回时可能需要在当前栈帧中保存一些信息,用来帮助它恢复上层方法的执行 状态
六.占内存溢出
当栈内存不足时就会发生占内存溢出-------------->可设置栈的大小来改变栈所占空间的大小
内存溢出的两种原因
1.栈帧过多导致内存溢出
2.栈帧过大导致内存溢出
栈溢出错误:
java.lang.StackOverflow
设置栈内存的大小:
-Xss size;
栈内存大小的默认值:
Windows(依赖虚拟机大小):1024k
七.本地方法栈
使用native修饰的方法为本地方法,它不是由java实现的,而是由本地动态库提供,可以是任意语言的实现
线程私有的
本地方法栈服务的是native方法
八.堆
储存所有的对象,变量,数组,堆区在jvm中只有一个是多线程所共享的
java堆内存的划分
1.堆是jvm中占用内存最大的一部分
2.堆是被所有线程共享的区域,在虚拟机启动时创建
3.堆是垃圾收集器管理的主要区域
4.堆在逻辑上分为新生代和老年代,新生代又分为 Eden 和 Survivor 区。Survivor 区由 FromSpace 和 ToSpace 组 成。Eden 区占大容量,Survivor 两个区占小容量,默认比例是 8:1:1。
对象的存放过程:
新创建的对象会放在新生代的Eden区,当Eden区的对象放满之后,会触发Minor Gc清理Eden区的对象,存活下来的对象会放在Survivor区的from区,当from区存放满之后会触发Minor Gc清理from区的对象,再次存活的对象会放到To区,这样保证在一段时间内总有一个Survivor区是空的,经过多次Minnor Gc后仍然存活的对象会被放到老年代中。
6.老年代储存长期存活的对象,沾满是会触发Major Gc或者Full Gc对整个对空间进行清理。
GC状态会停止所有的线程 等待GC完成,所以对响应要求高的应用 尽量减少发生 Major GC,避免响应超时
7.Minor GC : 清理年轻代
Major GC : 清理老年代
Full GC : 清理整个堆空间,包括年轻代和永久代
所有 GC 都会停止应用所有线程
8.堆所占用的内存是可扩展的,可通过“-Xms"或者”Xmx"来控制堆的最小内存和最大内存
JVM 堆内存常用参数
堆内存溢出
垃圾收集器会自动收集堆内存中不在使用的对象并回收,但如果在堆内存中不断产生新
对象,并且都被长期使用,这样就有可能发生堆内存溢出
九.方法区
方法区在逻辑上也属于堆的一部分,但是在具体实现上不强制要求方法区的位置
不同的虚拟机厂 商可以有不同的实现,如 JDK1.8 之前使用永久代实现,1.8 后使用元空间实现
方法区在虚拟机启动时创建,是所有线程所共享的
存储结构
方法区用于存储类的结构:
运行时常量池(含字符串常量)、静态变量、类的信息、常量。
类信息:
魔数,版本号,常量池,类(字段和方法),父类和接口数组,字段,方法等 信息
方法区是 JVM 中的一个规范、永久带和元空间是方法区的两个不同实现