目录
JVM运行流程
JVM内存区域划分
线程私有
Java虚拟机栈
作用:描述的Java方法执行的内存模型。
内存模型:每个Java方法执行的时候都会创建一个函数的栈帧,包括:局部变量表,操作帧,动态链接,方法返回地址。
局部变量表: 存储基本数据类型,对象引用。
操作帧:每个方法会生成一个先进后出的操作栈。
动态链接:执行运行时常量池的引用。
方法返回地址:PC寄存器的地址。
生命周期:和线程的声明周期一样。
本地方法栈
类似于Java虚拟机栈,但它是给本地方法使用的。
程序计数器
作用:用来记录当前线程执行的行号。
如果当前线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个本地 方法,这个计数器值为空。
以上三个部分的数据区域都是每个线程私有的,每个线程创建出这样的空间来。 而接下来的堆和元数据区则是线程之间共享的区域。
线程共享
堆
程序中所有创建的对象都在堆中。
堆中具体还会划分出几块不同区域,在垃圾回收部分详解。
元数据区(方法区)
存储被JVM加载的类信息、常量、静态变量。
在JDK1.8中,字符串常量池移到了堆中,之前是在元数据区。
总结一下:局部变量在栈,普通成员变量在堆,静态成员在元数据区。
JVM类加载
类加载是非常复杂的过程,这里只是简单介绍一下。
类加载过程
加载:把字节码文件找到(.class),读取文件内容。(具体由三个类加载器工作完成,使用双亲委派模型)
验证:根据JVM虚拟机规范,检查字节码文件是否符合要求
准备:给类对象分配内存空间(此时内存初始化状态全为0)
解析:针对字符串常量进行初始化,把常量池中的符号引用转为直接引用的过程。
符号引用:记录的不是真正的地址,而是相较于真正地址的偏移量。
直接引用:解析后,把字符串常量放到真正的内存中的地址
初始化:执行类的构造器,把对象初始化,加载父类,执行静态代码块等。
双亲委派模型
该模型需要三个类加载器,分别是
BootstrapClassLoader:负责加载标准库中的类
ExtensionClassLoader:负责加载JVM扩展库
ApplicationClassLoader:负责加载用户提供的第三方库/用户项目代码中的类
其中ApplicationClassLoader有一个属性指向ExtensionClassLoader,ExtensionClassLoaderr有一个属性指向BootstrapClassLoader
该模型的执行顺序:
垃圾回收机制
垃圾:不再使用的内存,以对象为单位。
找到垃圾
计数算法
该算法是Python、PHP使用的。
它给每个对象都分配了一个计数器(整数)
当刚创建出该对象时,计数器为1,此后如果有其他引用指向该对象,计数器++。
销毁一个引用计数器--。
当计数器为 0 时,该对象就是垃圾了。
优点:简单有效
缺点:1. 内存浪费的较多
2. 存在循环引用的问题,此时需要引入其他机制来解决该问题。
可达性分析算法
该算法是Java使用的。
Java中的对象都是通过引用来指向并访问的,引用中的成员又指向了其他对象。(形成树状结构)
JVM保存了所有对象。
每隔一段时间进行一下扫描,JVM从根节点(GCroots)出发,把可以访问得到的对象做个标记,不能访问到的全部当成垃圾回收了。
找到垃圾后就可以清除垃圾了。
清除垃圾
标记清除算法
把垃圾标记出来之后,直接清除掉。
这样会产生大量的空间内存碎片。空间虽然还有,但是能够正常使用的就很少了。
复制算法
把内存空间化为两部分:把非垃圾的部分复制到一边,然后直接清楚掉另一边的全部空间。
空间利用率还是较低,并且当有效对象较多时,复制成本比较大。
标记整理算法
刚开始和标记清除算法一样,但是它后面把有效对象都移动的一起。
提高了空间利用率,但是复制成本还是比较大。
分代算法
分代算法是整合了上述算法,让它们针对不同的对象适用不同的算法。
有什么错误评论区指出。希望可以帮到你。