目录
虚拟机?
JVM作用
JVM整体组成部分
一.类加载器
类加载过程
类什么时候会被加载(初始化)?
类加载器分类
1.引导类加载器
2.扩展类加载器
3.应用程序类加载器
4.自定义类加载器
双亲委派机制
打破双亲委派机制
二.运行时数据区
①程序计数器
②本地方法栈
③Java虚拟机栈
基本作用特征
运行原理
栈帧结构
④堆
基本作用特征
堆空间的分区:
为什么要分区
创建对象,在堆内存中分配的过程
分代收集思想 Minor GC、Major GC、Full GC
堆空间的参数设置
字符串常量池
⑤方法区
方法区,堆,栈交互关系
方法区大小设置
方法区的内部结构
方法区的垃圾回收
虚拟机?
在windows中,虚拟一个运行环境,分为系统虚拟机(VMware),程序虚拟机(JVM)
JVM作用
(1)负责将字节码加载到内存中(运行时数据区)
(2)负责储存数据
(3)把字节码翻译为机器码
(4)垃圾回收
JVM整体组成部分
(1)类加载器
(2)运行时数据区
(3)执行引擎
(4)本地方法接口
(5)垃圾回收
一.类加载器
作用:负责从硬盘/网络中加载字节码信息
加载到内存中(运行时数据区的方法中)
类加载过程
加载 ----> 链接 ----> 初始化
链接中包含: 验证----> 准备 ----> 解析
加载: 使用io读取字节码文件
转换并储存,为每个类创建一个Class类的对象
存储在方法区中
链接:
验证: 对字节码文件格式化进行验证,文件是否被污染
对基本的语法格式进行验证
准备: 为静态变量进行内存分配
如:public static int value = 123;
在准备阶段后的初始值是0,不是123
解析: 将符号引用转换为直接引用
将字节码中的表现形式,转为内存中的表现形式(内存地址)
初始化: 类的初始化,在类中定义的静态变量进行赋值
如:public static int value = 123;
在初始化阶段后的初始值是123
类什么时候会被加载(初始化)?
主动使用: 1.在类中运行main方法
2.创建对象new
3.使用类中的静态变量,静态方法
4.反射 Class.forName("类的地址")
5.子类被加载
误区:1.static final int b = 20;编辑期间赋值的静态常量
2.User [] users = new User[10]; 作为数组类型
类加载器分类
具体的负责加载类的一些代码
1.引导类加载器
用c/c++语言开发的, jvm底层的开发语言,负责加载java核心类库.
与java语言无关的.
2.扩展类加载器
java 语言编写的,由 sun.misc.Launcher$ExtClassLoader 实现,继承ClassLoader类.
从 JDK 系统安装目录的 jre/lib/ext 子目录(扩展目录)下加载类库
3.应用程序类加载器
Java 语言编写的,由 sun.misc.Launcher$AppClassLoader 实现. 派生于 ClassLoader 类.
加载程序中自己开发的类
4.自定义类加载器
双亲委派机制
加载一个类时 , 先委托给父类加载器加载, 如果父加载器没有找到 , 继续向上级委托 , 直到引导类加载器 . 父级找到就返回 , 父级如果最终没有找到 , 就委派给子级加载器 , 最终没有找到ClassNotFoundException.为了先确保加载系统类
双亲委派机制,是java提供的类加载的规范,但不是强制不能改变的.我们可以通过自定义的类加载器,改变加载方式.
打破双亲委派机制
可以通过继承ClassLoader类,重写loadClass/findClass方法,实现自定义的类加载
典型的tomcat中,加载部署在tomcat中的项目时,就使用的是自己的类加载器
二.运行时数据区
①程序计数器
是一块很小的内存空间,用来记录每个线程运行的指令位置,
是线程私有的,每个线程都拥有一个程序计数器,生命周期与线程一致
是运行时数据区中,唯一一个不会出现内存溢出的空间.
运行速度最快.
②本地方法栈
用来运行本地方法的区域
是线程私有
空间大小可以调整
可能会出现栈溢出
③Java虚拟机栈
基本作用特征
栈是运行单位,管理方法的调用运行
是用来运行java方法的区域.
可能会出现栈溢出.
是线程私有的.
运行原理
先进后出的结构
最顶部的称为当前栈帧,
栈帧结构
一个栈帧包含:
局部变量表(存储在方法中声明的变量)
操作数栈(实际计算运行)
动态链接
void A(){
B();//B方法的地址
}
方法返回地址
④堆
基本作用特征
是存储空间,用来存储对象,是内存空间最大的一块儿区域,
在jvm启动时就被创建,大小可以调整(jvm调优)
本区域是存在垃圾回收的.是线程共享的区域
堆空间的分区:
年轻代(新生区/新生代)
伊甸园区(对象刚刚创建存储在此区域)
幸存者1区 幸存者2区
老年代(老年区)
为什么要分区
可以根据对象的存活的时间放在不同的区域,可以区别对待.
频繁回收年轻代,较少回收老年代.
创建对象,在堆内存中分配的过程
1.新创建的对象,都存储在伊甸园区
2.当垃圾回收时,将伊甸园中垃圾对象直接销毁,将存活的对象,移动到幸存者1区,
3.之后创建的新对象还是存储在伊甸园区,再次垃圾回收到来时,将伊甸园中的存活对象移动到幸存者2区,同样将幸存者1区的存活对象移动到幸存者2区,每次保证一个幸存者区为空的,相互转换.
4.每次垃圾回收时,都会记录此对象经历的垃圾回收次数,当一个对象经历过15次回收,仍然存活,就会被移动到老年代
垃圾回收次数,在对象头中有一个4bit的空间记录 最大值只能是15,
5.老年区回收次数较少,当内存空间不够用时,才会去回收老年代.
堆空间的配置比例
默认的新生代与老年代的比例: 1:2 可以通过 -XX:NewRatio=2 进行设置
如果项目中生命周期长的对象较多,就可以把老年代设置更大.
在新生代中,伊甸园和两个幸存者区比例: 8:1:1 可以通过-XX:SurvivorRatio=8 进行设置
对象垃圾回收的年龄 -XX:MaxTenuringThreshold=<N>
分代收集思想 Minor GC、Major GC、Full GC
对年轻代进行垃圾回收称为 Minor GC /yong GC 是频繁进行的回收
对老年代进行垃圾回收称为 Major GC / old gc 回收的次数较少
Full GC 整堆收集 尽量避免
System.gc();时 程序员几乎不用
老年区空间不足
方法区空间不足
堆空间的参数设置
官方文档
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
字符串常量池
在jdk7之后,将字符串常量池的位置从方法区转移到了堆空间中,
因为方法区的回收在整堆收集时发生,回收频率低,
堆空间回收频率高.
⑤方法区
作用: 主要用来存储加载的类信息, 以及即时编译期编译后的信息, 以及运行时常量池
特点: 在jvm启动时创建,大小也是可以调整, 是线程共享,也会出现内存溢出.
方法区,堆,栈交互关系
方法区存储类信息(元信息)
堆中存储创建的对象
栈中存储对象引用
方法区大小设置
-XX:MetaspaceSize 设置方法区的大小
windows jdk默认的大小是21MB
也可以设置为-XX:MaxMetaspaceSize 的值是-1,级没有限制. 没有限制 就可以使用计算机内存
可以将初始值设置较大一点,减少了FULL GC发生
方法区的内部结构
类信息
以及即时编译期编译后的信息,
以及运行时常量池(指的就是类中各个元素的编号)
方法区的垃圾回收
在FULL GC时方法区发生垃圾回收.
主要是回收类信息, 类信息回收条件比较苛刻,满足以下3点即可:
1.在堆中,该类及其子类的对象都不存在了
2.该类的类加载器不存在了
3.该类的Class对象不存在了文章来源:https://www.toymoban.com/news/detail-555277.html
也可以认为类一旦被加载就不会被卸载了.文章来源地址https://www.toymoban.com/news/detail-555277.html
到了这里,关于JVM入门(1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!