JVM学习
一、JVM定义
JVM是Java Virtual Machine(java虚拟机)的缩写。引入jvm后,java语言在不同平台上不需要重新编译。jvm屏蔽了java语言与具体平台相关的信息,java语言只需要编译生成字节码就可以在多种平台上运行。
二、类加载子系统
1.作用
类加载子系统负责从文件系统或网络中加载class文件
类加载子系统只负责加载class文件,至于是否执行,由执行引擎决定
加载的类信息存放在方法区中,方法区中可能还存放运行时常量
2.类加载过程
类加载指的是将类的二进制数据读到内存中,将其放在运行时数据区的方法区内,然后堆内创建一个class对象,用来封装方法区内的数据结构,并提供访问方法区数据结构的接口。
3.类加载分类
启动类加载器
负责加载java_home/lib目录下可以被虚拟机识别的字节码文件
扩展类加载器
负责加载java_home/lib/ext目录下的字节码文件
应用程序加载器
负责加载classpath路径下的字节码
用户自定义加载器
需要继承classLoader并重写findclass方法。
4.双亲委派机制
如果一个类收到了加载请求,并不会自己加载,而是把请求交给父类加载器执行。如果父类加载器还有父类,依次向上委托,直到引导类加载器。如果顶层加载器加载成功,则成功返回。如果失败,则子类加载器会尝试加载,直到加载成功。
优点:避免类的重复加载;保护程序安全,防止核心api被篡改。
三、运行时数据区
1.内部结构
一个进程对应一个jvm实例,一个运行时数据区又包含多个线程,这些线程共享堆和方法区,每个线程包含程序计数器、本地方法栈和java虚拟机栈。
2.程序计数器
作用:用来指向下一条指令的地址,由执行引擎读取下一条指令。
3.栈
运行时的单位,解决程序的运行问题,即程序如何运行
存放基本数据类型的局部变量,以及数据类型的引用引用对象。
4.堆
存储的单位,解决数据存储的问题
对象主要存放在堆空间,是运行时数据区比较大的一块。
5.java虚拟机栈
每个线程创建时都会创建一个java虚拟机栈,其内部保存一个个栈帧,对于java一次次方法调用
作用:主管java程序的运行,保存方法的局部变量、部分结果、参与方法的调用与返回
不存在垃圾回收问题
栈的运行原理:
没个线程都有自己的栈,栈中的数据以栈帧的格式存放;
在这个线程上运行的每个方法对应各自的一个栈帧;
栈帧是一个内存区块,维系着方法执行过程中的各种数据信息;
JVM对java栈的操作有进栈和出栈;
一个线程上一个时间点上只有一个活动的栈帧。即只有当前正在执行的栈帧是有效的,被称为当前栈帧,与当前栈帧对应的方法叫做当前方法;
执行引擎运行的字节码只对当前栈帧操作;
如果该方法中调用了其他方法,对应新的栈帧会被创建出来,成为新的当前栈帧;
不同线程中包含的栈帧是不可以相互引用的;
如果当前方法调用了其他方法,方法返回之际,当前方法会传回此方法的执行结果给前一个栈帧,接着,虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧;
java的两种返回函数的方式,一种是正常的函数返回,使用return指令,一种是抛出异常。不管哪种方式都会导致栈帧被弹出。
栈帧内部结构:
局部变量表、操作数栈、动态链接、方法返回地址、附加信息
6.本地方法接口
定义:java调用非java代码接口,该方法的实现由非java语言实现。
原因:本地方法使用起来方便,实现容易或者要求效率高。
7.堆
堆的概念
一个进程对应多个jvm实例,同时包含多个线程,这些线程共享方法区和堆,每个线程都有独立的本地方法栈、程序计数器、java虚拟机栈。
一个jvm只有一个堆内存,堆是java内存管理的核心区域。
java堆区在jvm启动时就被创建,其空间大小也被确定,是jvm最大的一块内存。
堆可以是物理上不连续的但逻辑上应该被视为连续的。
所有的对象都应该被分配在堆上。
对象不能保存在栈上,因为栈中保存引用,这个引用指向对象在堆中的位置。
在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。
堆是垃圾回收的重点区域。
堆的内存结构:
逻辑:年轻代(伊甸园区、幸存者0区、幸存者1区)、老年代、元空间
存储在jvm中的对象可以被划分为两类:
一种是生命周期较短的瞬时对象,这类对象创建和消亡都很快(存入新生代)
二种是对象生命周期非常长,在某些情况还能与jvm的生命周期保持一致(存入老年代)
对象分配过程:
对象分配是非常严谨的任务,需要考虑内存如何分配、分配在哪里,并且内存分配算法与内存回收算法有关,所以还需考虑垃圾回收完成后内存空间是否产生内存碎片。
(1)new的对象放在伊甸园区,此空间有大小限制。
(2)当伊甸园空间满了,程序又要创建对象,jvm垃圾回收器对伊甸园进行垃圾回收,将伊甸园区中不再被引用的对象进行销毁。将伊甸园区剩余对象放到幸存者0区。
(3)加载新的对象放在伊甸园区
(4)如果再次出发垃圾回收,此时上次幸存下来存放在幸存者0区的对象,如果没有回收会被放入到幸存者1区。
(5)如果再次垃圾回收,此时后重新放回到幸存者0区。
(6)啥时候区养老区?可以设置次数,默认15次,可以设置次数。-XX:MaxTenuringThreshold=进行设置。
(7)在养老区,当内存不足时,再次触发GC,进行养老区内存清理。
(8)若养老区发生gc之后无法清理出空间,则会触发OOM异常。
堆空间分代思想
优化GC性能:如果没有分代,所有对象放在一块,GC时需要找出哪些对象没用,这就会对堆的所有对象进行扫描,而很多对象是朝生夕死。如果分代,就可以把新创建的对象放在一个地方,当gc时就可以直接在这个朝生夕死的地方进行回收,就会腾出很大地方。
四、执行引擎
JVM主要任务是装载字节码到其内部,但字节码不能直接在操作系统上运行,因为字节码指令不等于本地机器指令。执行引擎的任务是将将字节码解释/编译成为本地机器可以执行的指令。
java语言是半编译半解释语言原因:java程序在运行之前有一个编译过程,但并不是将将编译程序转换成机器语言,而是将它编译成字节码,然后通过执行引擎将其解释为机器指令运行。
五、垃圾回收机制
1.定义:垃圾是指在程序运行过程中没有任何指针指向的对象,这个对象就需要被垃圾回收。如果这些垃圾不回收,那么这些垃圾所占用的内存会一直保留到应用程序结束,被保留的空间无法被其他对象使用,甚至导致内存溢出。
2.垃圾回收算法
对象存活判断:引用计数法和可达性分析法
引用计数法:某个对象只要被引用一次,对应的引用计数器加一,当引用失效时引用计数器减一。缺点存在循环引用问题。
可达性分析法:从根对象为起点,自上而下搜索被跟对象集合所连接的目标对象是否可达。如果目标对象没有被任何引用链相连,则表示不可达,为垃圾。
清除算法:
标记清除算法、复制算法、压缩算法
标记清除算法:容易产生内存碎片
复制算法:需要两倍的内存空间
原文:https://blog.csdn.net/qq_48435252/article/details/123697193