一、JVM是什么?
jvm是java虚拟机的缩写,是java程序运行的核心组件。jvm是模拟计算机,他在物理计算机上模拟了一个执行Java字节码的环境,并负责执行字节码。所以他实现了跨平台性和代码安全性。
二、JVM执行流程(JVM的工作机制)
1.类加载:JVM首先加载Java字节码文件,字节码文件通常以.class文件的格式存在,类加载器将字节码文件加载到内存中,并进行验证、准备、解析、初始化操作。
1.1 双亲委派机制:当一个类收到了类加载请求,当前类加载器首先不会尝试自己去加载这个类会从已经加载过的类中查询已经在则返回已经加载过的类。未加载委派给父类去加载,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中。只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的 Class),子类加载器才会尝试自己去加载。启动类加载器加载失败,就会使用扩展类加载器来加载,继续失败就会用AppClassLoader来加载,继续失败就会抛出异常:ClassNotFoundException。
1.2 加载、验证、准备、解析、初始化
加载:根据类的全限制名(绝对路径),找到指定的字节码文件,在内存中生成一个java.lang.Classs的对象,存放在方法区。
验证:确保字节码文件中信息符合Class文件格式规范。
有文件格式验证 :基于二进制字节流进行分析
元数据验证(类层面验证):对类的元数据进行语义分析
字节码验证(方法层面验证):对方法体语义分析
符号引用验证:根据引用是否找到对应的类、方法、字段
准备:在方法区中为类的静态变量分配内存并初始化
解析:把常量值中的符号引用替换为直接引用
初始化:执行类构造器<client>方法。
2.字节码解释与执行:JVM将加载到内存中的字节码解释成机器码,并按照指令依次执行。
3.运行时内存区域管理里:JVM将运行时内存划分为不同区域,包括方法区、堆、虚拟机栈、程序计数器、本地方法栈,分别存储类信息、对象实例、方法调用栈、线程指令地址。
4.垃圾回收:JVM通过垃圾回收器自动管理内存,回收不再使用的对象,释放内存空间。垃圾回收器会根据一定的策略判断对象是否可回收,并进行相应的回收操作。
5.异常处理:JVM提供了异常处理机制,当程序中发生异常时,JVM会捕获并处理异常。在堆栈中找到适合的异常处理器,执行异常处理代码。
三、JVM内存模型
1.程序计数器:内存较小的一块内存,线程私有可以看作当前线程执行字节码的行号指示器
作用:
字节码解释器通过改变程序计数器来控制程序执行,如:顺序执行、循环、异常处理等
多线程情况下,记录当前线程的执行位置,线程被切换回来时可以知道线程运行到哪里
不会产生oom异常,生命周期和线程一样
2.虚拟机栈:也叫方法栈,线程私有,描述java方法执行的内存模型。由一个个栈帧组成,每个栈帧包含:局部变量表、操作栈数、动态链接、返回地址组成。每个方法的执行都会创建一个栈帧,方法的开始与结束可以理解为栈帧的入栈与出栈。
局部变量表:包含八大基本类型、返回地址、对象引用
操作栈数:存放临时变量、运算
会产生两种异常,在栈内存不允许动态扩容时,线程请求深度大于栈最大深度时抛出StackOverFlowError异常。线程请求栈没内存时产生oom异常。
3.本地方法栈:与虚拟机栈相似,执行的是本地方法
4.堆:线程共享内存,创建对象和数组存放在里面。垃圾回收的重要内存区域。
新生代
伊甸区(Eden):java新对象的出生地(如果新对象占用内存较大,直接放在老年代),当该区域内存不足时会触发轻GC.
幸存0区(SurvivorFrom):上一次GC的幸存者,作为这一次GC的被扫描者
幸存1区(SurvivorTo):保留一次,一次轻GC的幸存者
轻GC(MinorGC)过程 (复制->清空->互换)
将伊甸区、幸存0区复制到幸存0区,年龄+1,年龄达到老年标准放到老年区,如果幸存1区内存不够直接放到老年区。
清空伊甸区、幸存0区中的对象
幸存0区与幸存1区互换
老年代
老年代不会频繁触发重GC(MajorGC),一般重GC前会发生一次轻GC,新生代晋升老年代空间不够时触发,无法找到足够的空间存放大对象时触发。
重GC使用标记清除法,遍历查询所有对象,标记处存活对象,清除所有未标记对象。耗时较长,且会产生碎片内存。所以要做合并或者标记方便下次直接分配。在老年代也内存不够时会产生oom异常
5.方法区/永久代:用于存储被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
此区域可以选择不进行垃圾回收,垃圾回收的主要目标是常量池的回收、类型的卸载。
常量池:存放编译期生成的各种字面量和符号引用,在类加载后存放到方法区的常量池中。
避免频繁的创建和销毁对象而影响系统的性能,实现了对象的共享。
java8中永久代被元空间取代。元空间并不在虚拟机,而是用本地内存。类的元数据放在本地内存,字符串池、类的静态变量放到java堆中。
四、垃圾回收
垃圾回收机制是内存管理核心功能之一,java自动管理对象生命周期,回收不再使用对象占用的内存。基本原理:标记、清除、压缩
1.如何确定垃圾
1.1 引用计数法
引用和对象是有关联的,如果要操作对象则必须用引用进行。所以通过引用计数来判断对象是否可以回收。
2.2 可达性分析
为了解决引用计数法的循环引用问题,采用可达性分析方法,通过一系列GCroots对象作为起点搜索。如果GCroots和一个对象没有可达路径,则对象不可达。不可达对象不等于可回收对象,对象至少经过两次不可达标记才会面临回收。
2.垃圾回收算法
2.1 标记清除算法
标记阶段:采用可达性分析标记所有可达对象
清除阶段:遍历清除未标记的对象
缺点:产生大量的内存碎片
2.2 复制算法
将内存分为两个区域,一次只使用一个区域,内存满后将保留对象顺序复制到未使用区域,清空使用过的区域。
优点:不会产生内存碎片
缺点:需要使用双倍内存,保留对象较多时,需要大量复制,效率降低
2.3 标记整理/标记压缩算法
结合标记清除算法和复制算法的优点。他标记出所有存活的对象,将存活对象移动到堆的一端,清除边界以外对象。
2.4 分代算法
根据对象的存活的不同生命周期将内存划为不同区域,不同区域采用不同算法。青年代采用复制算法,老年代采用标记清除算法或标记压缩算法。
3.垃圾收集器
4.GC调优