吴忠市建设局官方网站,app软件开发制作公司,wordpress 登入不进去,微信小程序定位权限怎么打开什么是JVM
java虚拟机
JVM的功能
1.解释和运行
对字节码文件中的指令#xff0c;实时的解释成机器码#xff0c;让计算机执行
2.内存管理
自动为对象、方法等分配内存
自动的垃圾回收机制#xff0c;回收不再使用的对象#xff08;c不会自动回收#xff0c;相当于降…什么是JVM
java虚拟机
JVM的功能
1.解释和运行
对字节码文件中的指令实时的解释成机器码让计算机执行
2.内存管理
自动为对象、方法等分配内存
自动的垃圾回收机制回收不再使用的对象c不会自动回收相当于降低了编程的下限
3.即时编译JIT
对热点代码进行优化提升执行效率
java需要实时解释是为了不同平台的兼容性然而由于需要实时编译所以性能方面比不上c所以就需要即时编译
即时编译的过程 把热点代码就是频繁出现的代码放到内存这样下次就不用再编译了提高性能。
java虚拟机的组成
1.类加载器加载字节码文件到内存
2.运行时的数据区域JVM管理的内存 负责管理jvm使用到的内存比如对象的创建和销毁
3.执行引擎即时编译器、解释器、垃圾回收器等将字节码内的指令解释成机器码同时视同JIT即时编译器优化性能
4.本地接口调用本地编译的方法比如c/c实现的方法 native方法 字节码文件的组成
jclasslib查看字节码文件
字节码文件的组成 魔数java字节码文件将文件头称为魔数
主副版本号jdk1.2是46 之后每升级一个大版本就1 比如jdk1.8就是466 52
主版本号作用就是判断当前字节码的版本和运行时的jdk是否兼容
常量池避免相同的内容重复定义节省空间
类的生命周期 五个阶段加载、链接、初始化、使用、卸载
加载阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息第二步类加载器加载完类以后java虚拟机就会把字节码中的信息放到一个内存区里面就是方法区第三步生成一个INstanceklass对象保存类的所有信息里面还包含实现特定功能的信息如多态信息。 第四步同时java虚拟机还会在堆中生成一份与方法区中数据类似的java.lang.class对象。
作用是在java代码中去获得类的信息一级存储静态字段的数据。 问既然都方法区都有信息了为什么还要在堆区存储信息
答方法区中的instanceklass是用c实现的对于我们java程序是不好直接操作的。所以我们要转换为java.lang.class对象才好操作。并且堆区的字段少于instanceklass不是所有字段都需要访问的所以要控制开发者的访问范围
连接阶段有三个小阶段 验证、准备。解析。
验证检验程序的内容是否符合java虚拟机的规范 准备准备阶段为静态变量static分配内存并设置初始值这里的初始值是指默认值 不是你赋的值 比如int的初始值就是0。
但是如果是final修饰的静态变量的话在准备阶段就会赋你给定的值不用等到初始化阶段。 解析阶段将常量池的符号引用替换成直接引用。
符号引用在字节码文件中使用编号来访问常量池的内容。
直接引用不再使用编号直接使用内存的地址进行访问具体数据 初始化阶段会执行静态代码块中的代码并为静态变量赋值。
从字节码角度分析就是执行字节码文件中clinit部分的字节码指令 初始化阶段不一定存在 出现继承的初始化阶段 final修饰的变量如果赋值的内容需要执行指令才能出结果那么就会执行clinit指令进行初始化
类加载器
java虚拟机提供给应用程序去实现获得类和接口字节码数据的技术 类加载器分为两类 一类是java代码实现 一类是java虚拟机底层源码实现的 类加载器在jdk8和8之后的差别很大 启动类加载器用于加载java安装目录/jre/lib下的类文件
可以帮我们扩展我们要用的核心类
1.放入jre/lib进行扩展不推荐使用
2.使用参数进行扩展 扩展类加载器用于加载/jre/lib/ext下的类文件
类似于启动类加载器 我们也可以通过扩展类加载器去加载用户的jar包 应用程序类加载器用于加载classpath下的类文件
类加载器的双亲委派机制重点
这东西的核心就是解决一个类到底由谁加载的问题有多个类加载器
作用
1.保证类加载的安全性
2.避免重复加载 双亲委派机制就是当一个类加载器接收到加载累的任务时会自底向上查找是否加载过再自顶向下进行加载。 现在思考一个问题
如果一个类 三个加载器都能加载 那应该谁加载启动类加载器
面试题 打破双亲委派机制
三种方式自定义类加载器 线程上下文类加载器 Osgi框架的类加载器 为什么要打破 自定义类加载器打破双亲委派机制 双亲委派机制的核心代码是在loadclass方法里面只要重写这个方法把核心代码删了就可以打破双亲委派机制了。 自定义类加载器默认的父类加载器是应用程序类加载器
问题 但是考虑到loadclass方法是通过调用findclass实现的双亲委派机制所以真正实现一个自定义类加载器的方式是重写findclass方法。这样就不会破坏双亲委派机制并且创建一个新的自定义类加载器。
线程上下文类加载器
SPI机制是JDK内置的一种服务提供发现机制 思考一个问题spi机制是如何拿到应用进程的应用程序类加载器的因为DriveManger是由启动类加载器加载的但是SPI最后却可以用应用进程类加载器加载DriveManger OSGI框架打破双亲委派机制 热部署 注意事项 JDK9之后的类加载器
JDK9引入了module概念 类加载器的设计发生了变化 jdk的类不再位于jar包中而是放到一个jmod文件夹里面
启动类加载器不再使用c编写直接使用java编写位于jdk.internal.loader.ClassLoaders 拓展类加载器变成平台类加载器 由于JDK9之后是用模块化的设计思路所以其实平台类加载器是没什么用的它的存在只是为了和老版本兼容 JVM的第二部分运行时数据区域JVM管理的内存 运行时数据区分为 程序计数器、java虚拟机栈、本地方法栈这些都是线程不共享的。
方法区、堆线程共享的 程序计数器也叫pc寄存器每个线程都会通过程序计数器记录当前要执行的字节码指令的地址
作用
1.控制程序指令的进行实现分支、跳转、异常等逻辑
2.在·多线程的情况下可以保存当前指令的内存地址以确保下一次的执行
内存溢出程序在使用某一块内存区域的时候存放的数据需要占用的内存大小超过了虚拟机能提供的内存上限。 思考一下 程序计数器会不会出现内存溢出
当然不会 每个线程只存储一个固定长度的内存地址程序计数器是不会发生内存溢出的 程序员不需要对程序计数器做任何处理java虚拟机实现的 栈 本地方法栈是由c实现的方法 java虚拟机栈是由java实现的 java虚拟机栈随着线程的创建而创建而回收则在线程销毁时进行。由于方法可能会在不同线程中执行每个线程都会包含一个自己的虚拟机栈 栈帧的组成局部变量表 操作数栈 帧数据 局部变量的作用是在方法执行过程中存在所有的局部变量。编译成字节码文件时就可以确定局部变量表的内容 操作数栈
存放临时数据一般都是把临时数据存到操作数栈中当要用到这个数据了 就会取出来 存到局部变量表中。在编译期就可以确定操作数栈的最大深度。 帧数据
动态链接 当我们在使用别的类的方法和属性时在链接阶段是不会把fai该符号引用变成直接引用的所以我们要用动态链接 吧这个符号引用的内存地址保存在运行的常量池里面 方法出口
方法出口是指方法在正确或异常结束时当前栈帧会被弹出同时程序计数器应该指向上一个栈帧中的下一个指令的地址。所以在当前栈帧中需要存储此方法的出口地址简单来说就是在一个方法被弹出栈之前他会告诉你下一个方法的栈帧执行到哪里了
异常表
异常表存放代码中异常的处理信息包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置简单来说就是存放trycatch等处理异常的执行流程 那么问题来了 Java虚拟机栈会出现内存溢出吗
答案是有可能的 死递归就是栈内存溢出吧
java虚拟机栈的默认大小是根据操作系统和计算机体系结构决定的 一般都不会栈内存溢出 一般都是程序员操作失误 写了一个死递归才会这样
设置虚拟机栈大小 一般情况下工作中使用了递归操作栈的深度也就几百不会出现栈溢出的情况所以可以设置成-Xss256k节省内存。 本地方法栈 堆 创建出来的对象都在堆上空间占用最大的一块区域
在栈内存里面会保存这个对象在堆内存里面的地址这样就可以调用了。 堆内存会不会溢出
会的 堆内存有三个要关注的值used total max 其实不是滴堆内存的溢出判断条件比较复杂会在《垃圾回收器》中详细讲
手动设置max和total 在开发中直接把max和total设置成相同的数值这样不用重复申请减少开销 方法区
方法区存放基础信息的位置、线程共享
主要包括三部分类的元信息、运行时常量池、字符串常量池 这里的常量池静态常量池一般都是放引用 不是放数据 真正的数据在运行时常量池
运行时常量池 方法区在每个虚拟机上的实现不是都一样的 方法区的溢出 不停往方法区添加类的信息就可以了 字符串常量池
放的就是字符串常量 字符串常量池和运行时常量池有什么关系
在早期设计中字符串常量池是运行时常量池的一部分他们存储的位置都是一样的后面做了拆分 但如果是这样 返回的结果就是true
区别就在于 String的intern()方法可以手动将字符串放到字符串常量池中 jdk6的版本 jdk7及以后的版本 这里要注意的是字符串常量池里面的java是在虚拟机加载的时候就已经被加载进来了因为虚拟机要用到。所以java资格字符串不是后面加入的
静态变量到底存放在哪里
jdk6和之前的版本是放在方法区也就是永久代中。
jdk7以后的版本就放在堆里面的class对象中脱离了永久代 直接内存
直接内存不在java虚拟机的规范中所以并不属于java运行时的内存区域
主要是为了解决一个特定的问题 想要在直接内存中创建数据 可以使用ByteBuffer 直接内存也是可以设置大小的 思考 运行时数据区分为哪几部分每一部分的作用是什么 不同JDK版本之间运行时数据区域的区别是什么
jdk6 jdk7 jdk8 自动垃圾回收
在c/c没有自动垃圾回收如果一个对象不再使用需要手动释放否则就会出现内存泄漏。我们称释放对象的过程为垃圾回收。
内存泄漏指的是不再使用的对象在系统中未被回收内存泄漏的积累可能会导致内存溢出。 java的内存管理
java为了简化对象的释放引入了垃圾回收GC机制。通过垃圾回收期对不再使用的对象完成自动的垃圾回收垃圾回收期主要负责对堆上的内存进行回收。 自动垃圾回收方法区和堆主要是堆 思考一下 运行时的数据区有五部分程序计数器 java虚拟机栈 本地方法栈 方法区 堆 那么为什么GC只对方法区和堆进行回收
因为程序计数器 java虚拟机栈 本地方法栈是线程不共享的它们随着线程的创建而创建随着线程的销毁而销毁。而方法的栈帧在执行完方法之后就会自动弹出栈并释放掉对应的内存
方法区的回收
主要回收不再使用的类
一个类可以被卸载有三个条件 堆回收
什么对象可以被回收如果对象被引用了就不能回收 再思考一下如果上图的a1null b1null 那么可不可以回收A和B对象呢
可以的 方法中已经没有办法使用引用去访问A和B对象了即使他们的属性有相互引用 常用的两种判断方法引用计数法和可达性分析法 引用计数法会为每一个对象维护一个引用计数器当对象被引用的时候加1取消引用的时候减1 那么这个的缺点就很明显了那么java是不是解决了这个循环引用的问题 其实不是 java是用了另外一个方法可达性分析算法
可达性分析算法
可达性分析算法将对象分为两类垃圾回收的根对象GCRoot和 普通对象对象与对象之间存在引用关系 首先确定的是这个GC Root对象是不可以被回收的java会给一个GCRoot表去保存这些GcRoot 什么对象可以被称为GCRoot对象 这里关联栈内存的线程对象是主线程对象
可达性软法的引用的强引用除了强引用以外java中还有很多引用
软引用
弱引用
虚引用
终结器引用 软引用
软引用是比较弱的引用关系如果一个对象只有软引用关联它当程序内存不足的时候就会将软引用中的数据进行回收 由于软引用可以被回收所以软引用一般不用于引用重要的数据所以一般用于缓存。 如果软引用的引用对象被回收了那么我们是不是应该把软引用对象也回收掉
答案是肯定要回收的那么怎么回收 弱引用
与软引用基本一致区别就是弱引用包含的对象在垃圾回收机制时不管内存够不够都会直接被回收。 虚引用和终结器引用
不会在开发使用 垃圾回收算法
核心思想
1.找到内存中存活的对象
2.释放不再存活对象的内存使得程序能再次利用这部分空间
有四种垃圾回收算法标记-清除算法 复制算法 标记-整理算法 分代GC 评价标准1.吞吐量 2.最大暂停时间 3.堆使用效率 标记回收算法
分为两个阶段
标记阶段将所有存活的对象进行标记。java中用可达性分析算法从GCRoot开始通过引用链遍历出所有存活对象。
清除阶段从内存中删除没有被标记也就是非存活对象 优缺点 复制算法
把整个堆内存的空间分为From和To空间每次在对象分配阶段只能使用其中一块空间From空间。 优缺点 标记整理算法
也叫标记压缩算法是对吊机清理算法中容易产生内存碎片问题的一种解决方案。 分代GC算法-----分代垃圾回收算法
分代GC会把整个内存区域分为年轻代存活时间短的对象和老年代存活时间长
年轻代中还有几个小区 调整内存的大小 回收的步骤 为什么分代GC算法把堆分成年轻代和老年代 1.可以通过调整年轻代和老年代的比例适应不同类型的应用程序提高内存的利用率和性能。
2.新生代和老年代使用不同的垃圾回收算法新生代一般选择复制算法老年代可以选择标记-清除和标记-整理算法由程序员来选择灵活度较高。
3.分代的设计中允许只回收新生代如果能满足对象分配的要求就不需要对整个堆进行回收full gcSTW时间就会减少。 垃圾回收器 年轻代-serial垃圾回收器
serial是一种单线程串行回收年轻代的垃圾回收器 老年代-SerialOld垃圾回收器
SerialOld是Serial的老年代版本也是采用单线程串行回收 用的是标记-整理算法 年轻代-ParNew垃圾回收器
ParNew就是对Serial在多线程CPU下的优化使用多线程进行垃圾回收 老年代-CMSConcurrent Mark Sweep垃圾回收器
CMS垃圾回收器关注的是系统的暂停时间允许用户线程和垃圾回收线程在某些步骤中同时执行减少了用户线程等待的时间。 过程 那么有什么缺点呢 年轻代-Parallel Scavenge垃圾回收器
ps是JDK8默认的年轻代垃圾回收器多线程并行回收关注的是系统的吞吐量。具备自动调整堆内存大小的特点。 老年代-Parallel Old垃圾回收器 G1垃圾回收器 G1垃圾回收有两种方式
1.年轻代回收Young Gc
2.混合回收Mixed GC 年轻代和老年代都要回收 G1在进行YoungGC 的过程中会去记录每次垃圾回收时每个Eden区和Survivor区的平均耗时以作为下次回收时的参考依据。这样就可以根据配置的最大暂停时间计算出本次回收时最多回收多少个Region区域了。 混合回收 这里和cms长的差不多但是有点区别 cms的最终标记阶段会把上一个用户线程新创建的对象也标记但是G1这里不会 并且最后的用户清理 G1用的是复制算法 不会产生内存碎片。但是cms用的是标记清除算法 FULL GC 如果FULLGC都进行不了就会产生内存溢出 1.方法区一般不需要回收jsp等技术会通过回收类加载器去回收方法区中的类
堆就由垃圾回收器回收