一.什么是jvm
jdk-Java开发工具包
jre-Java运行时环境
jvm-Java虚拟机
像C++这样的语言就是直接编译成了二进制的机器指令,不同的cpu上面支持的指令不一样,如果是换了操作系统,可能就会需要重新编码.
而Java就想要只是使用一套编码,在各个操作系统上都是使用一套编码.
我们先通过javac把.java文件转换成.class文件(字节码文件,包含的就是Java字节码.字节码就是Java自己搞的一套cpu指令).然后在某个具体的系统上执行.此时通过jvm将Java自己的字节码文件(二进制指令)转换成对应的cpu能识别的机器指令.
在这中jvm就像是一个翻译官.负责将Java字节码文件转换成对应的系统的cpu可执行的指令.
当前的主流的jvm:HotSpot VM
二.JVM中的内存区域划分
jvm其实也是一个Java进程.进程在运行的过程中需要从操作系统申请资源.
如果定义一个变量,就会申请,变量申请到的内存就是jvm从操作系统中申请的内存
jvm申请到空间后会划分出几个区域,每个区域都有不同的作用
1.堆:代码中new出的对象,对象中持有的非静态成员变量都会存在堆区中,一个jvm中只有一份
2.栈:包含了方法的调用关系,成员变量也在栈区中,一个jvm中可能有多份
3.程序计数器:存储下一条要执行指令的地址(表示接下来 要干啥)
4.元数据区:一个程序中有哪些类,每个类中有哪些方法,每个方法包含哪些指令
三.JVM的类加载机制
类加载指的是,把.class文件从硬盘上读取到内存中,并进行验证和解析的过程.
类加载大致可以分为5个步骤
1.加载:把硬盘上的.class文件找到,打开文件,读取到文件内容(这里读到的就是二进制文件)
这里我们如何查找.class文件 :双亲委派模型
jvm中进行类加载的操作,有一个专门的模块,就是"类加载器"(ClassLoader)
jvm中的类加载器默认是有三个的
类加载器的作用,给一个"全限定类名",找到对应的.class文件
"全限定类名":java.lang.String(带有包名的类名)
BootstrapClassLoader(负责扫描标准库的目录)
ExtensionClassLoader(负责查找扩展库的目录)
ApplicationClassLoader(负责当前项目代码目录以及第三方库目录)
上述三个类加载器的关系就是:BootstrapClassLoader是爷爷,ExtensionClassLoader是爸爸
ApplicationClassLoader是儿子
双亲委派模型的工作流程:
上述设定就是为了确保这几个类加载器的优先级
也可以有效避免自己写的类和标准库类的名字重复,导致标准库的功能失效
我们可以通过自己写类加载器来破坏上述的规则
2.验证:验证.class文件是否合法3.准备:给类对象申请内存空间
4.解析:Java虚拟机中将常量池的符号引用替换为直接引用的过程,也就是初始化常量的过程.
符号引用:在文件中不存在地址这样的概念.此处文件中填充给s的"hello"的偏移量就是"符号引用"
接下来把文件加载到内存中.此时"hello"就有地址了,此时s将偏移量转换为真正的地址
5.初始化:针对类对象完成初始化
四.JVM中的垃圾回收算法(GC)
垃圾回收算法的主要目的是释放内存
引入垃圾回收算法后就不再需要我们自己手动释放内存,程序会自己判定某个内存是否会继续使用,如果不用就会释放掉.
垃圾回收算法的问题:STW(stop the world)问题触发垃圾回收机制可能会使代码的其他业务逻辑被迫暂停
在JVM的内存划分中,垃圾回收的主战场就是jvm的堆区垃圾回收,本质上就是回收对象
1.垃圾回收具体是怎样展开的?
(1)识别出垃圾,哪些是垃圾,哪些不是垃圾
如果一个对象没有一个引用指向它,就视为无法被代码使用,于是就会被回收
成员变量在出了方法的括号后就会被销毁
像下方动画演示,t在方法执行完毕后就被销毁了,此时就没有引用指向new Test()
所以new Test就被回收了
如果有更多的引用指向同一个对象,情况不好办了

(a)解决办法
●1引用计数
这种思想方法,并没有在JVM中使用,一般Python/PHP会使用

●可达性分析(JVM使用的是这个)
(2).把标记为垃圾的对象进行释放
(a).标记_清除
把标记为垃圾的对象直接释放掉
(b)复制算法
规避了内存碎片问题,但是占用了更多的内存空间
(c)标记-整理
虽然解决了内存碎片的问题,但是开销却很大
(3)分代回收(important)
引入对象的年龄的概念.
JVM中,有专门负责扫描的线程.一个对象,如果被扫描了一次,不是垃圾(不用回收),年龄+1
JVM会根据对象年龄的差异把整个堆的部分分为两个大的部分
新生代(伊甸区,两块生存区):年龄较小的对象和老年代:年龄较大的对象
(a)当我们new一个新的对象,就会存储在伊甸区中,按照规律,伊甸区的对象大部分活不过第一轮GC,大部分都是"朝生暮死",生命周期很短
(b)第一轮GC完事之后,少数在伊甸区存活下来的就会通过复制算法,拷贝到生存区.后续线程还会对伊甸区进行扫描.同时还会扫描生存区域.生存区也会大部分被标记为垃圾,少部分存活的,通过复制算法复制到另一个生存区中.只要是这个对象能够在生存区中继续存活.就会被复制算法再拷贝到另一个生存区中.每经历一次GC扫描.对象的年龄就会+1.
(c)如果这个对象经历了几次扫描依然建在.JVM就会把他从生存区复制到老年代.
(d)老年代对象也会被GC扫描,但是扫描的频次会大大降低.
(e)对象在老年代寿终正寝.此时JVM也会按照标记-整理的方式释放内存