jvm学习笔记。搞懂jvm各个知识点
1 jvm模型图
首先放上一张jvm模型图,方便后续理解知识点
jvm整体组成部分
- 类加载器(classLoader)
- 运行时数据区(Runtime Data Area)
- 执行引擎(Execution Engine)
- 本地库接口(Native Interface
2 jvm加载类的执行流程
java被编译成.class文件到jvm的类加载器中,类加载器经过一系列的方法将加载的文件放入运行时数据区中最终被销毁(GC)
2.1 jvm类加载器
2.1.1 类加载器说明
类加载器分为 启动类加载器,扩展类加载器,系统类加载器,自定义加载器
2.1.2 类加载的机制及过程
jvm会通过加载,链接,初始化这三个步骤对类进行初始化。 而链接可以分为三个步骤 验证,准备,解析
1. 加载过程名词解释
加载:
- 通过一个类的全限定名获得此类的二进制流
- 将这个字节流的数据结构转化为方法区运行时的结构数据
- 在内存中生成这个类的.class对象,作为方法区访问该类的入口
链接过程:
- 验证:确保加载的类信息符合jvm的规格,
- 准备:准阶段会为类的静态变量分配内存,赋初始值
- 解析:将符号引用转化为直接引用的过程
初始化:
- 执行类构造器clinit()方法的过程。加载静态代码块和类变量赋值。
2.1.3 类加载器介绍
类加载器分为 启动类加载器,扩展类加载器,系统类加载器,自定义加载器
- 启动类加载器(bootstrap class loader):加载java的核心类,使用c++语言实现。
- 扩展类加载器 (Extension ClassLoader) :负责加载JRE扩展目录(jre、lib、ext)下的jar包
- 系统类加载器(System ClassLoader) : 负责加载classpath路径下的类
- 自定义加载器 ()开发人员可以自定义加载器,自定义加载器继承classLoader类,重写findClass()方法
2.1.4 双亲委派机制介绍
类加载收到加载请求以后并不会直接进行加载,而是将该请求委派给它的父类加载器进行加载,如果父类也存在父类,再次向上进行委派,直到启动类加载器进行加载,如果成功则返回,如果无法加载,则由当前类进行加载。
优势在于:避免重复加载。而且保证了java核心api中定义的类型不会被随意替换,假设顶一个java.long.String的类,当通过双亲委派机制传递到启动类时,在启动类会在javaAPI中发现这个类。发现该类已经被加载,就直接返回API中的String类,而不会去加载自定义的String类
3 解析JVM运行时数据区的名词
1:方法区(Method Area)
方法区是所有线程共享的内存区域,用于储存已被Java虚拟机加载的类信息,常量,静态变量,编译后的代码
2:java堆(Java Heap)
java堆事java虚拟机中最大的块内存区域,它和方法区一样是所有线程共享的一块内存区域,还可以细分为新生代(young generation)和老年代(old generation),java堆的主要作用是存放对象的实例,几乎所有对象的实例都会在这里分配内存,需要注意它不是线程安全的,当多个线程同时访问对象时需要使用同步机制来保证线程的安全
以下是java堆的主要作用
- 对象实例的存储:java堆是java中用于动态分配内存的区域,当我们在程序中创建一个对象时,jvm会在堆内存中为该对象分配空间,这个空间的大小是程序运行期间动态决定的。取决于对象实际的大小
- 垃圾回收:java堆是垃圾收集器(garbage collector)的主要工作区域,垃圾收集器会自动收集那些不在被引用的对象,释放它们占用的内存空间。
- 内存分配与回收:jvm通过java堆来动态分配和回收内存,当程序需要创建新的对象时jvm会在堆中分配内存,当对象不再被引用的时候,垃圾收集器会回收这些内存
- 性能调优:可以通过调整java堆的大小和配置来优化程序的内存使用效率和响应时间,调整新生代和老年代可以优化垃圾收集器的性能
3:Java虚拟机栈(java virtual machine stacks)
主要用于支持方法执行和线程切换。每个线程在jvm中都有一个私有的栈,用于储存线程执行方法时的状态,包括局部变量,操作数栈,动态链接,方法出口信息
- 储存局部变量:当线程执行一个方法时,jvm会在栈上为该方法创建一个栈帧(stack frame),这个栈帧包含了该方法的局部变量表,用于储存方法的参数和局部变量,这些局部变量在方法执行期间时私有的,每一个线程都有自己的局部变量表
- 执行方法:jvm栈通过压栈(push)和出栈(pop)操作来执行方法,当一个方法被调用时,这个被调用的方法放在栈顶,直到有新的方法进入,再被往下压一层。
- 支持线程切换:每个线程都用自己的jvm栈,jvm栈会保存当前线程的状态,在线程上下文切换中使用栈中的状态来恢复线程的执行。
4:java本地方法栈(native method stack)
它用于支持带了native关键字的方法的执行。储存了每个native方法的状态信息,如方法参数,局部变量等
5:程序计时器(program counter register)
程序计时器是一个比较小的内存空间,主要用于存储需要执行的字节码指令地址
- 指令执行顺序:程序计时器确定了当前线程所执行的字节码指令的地址,jvm通过程序计时器来顺序执行字节码指令
- 多线程切换:在jvm中每个线程都有自己独立的程序计时器,当线程切换时,jvm会保存当前线程的程序计时器,并在该线程切换回来时来恢复它的执行
- 线程同步:程序计时器还可用于实现线程的同步
6:执行引擎
执行引擎时虚拟机的核心组件,它负责执行虚拟机的字节码,将字节码指令转换为机器码
7:本地库方法接口
指的是使用native关键字修饰的方法,这些方法代码并不是java编写的
8:本地方法库
指的是本地方法接口的集合,包含了多个本地方法
3.1 关于java堆的补充说明
java堆内存空间分为新生代和老年代,两者占用空间比例默认是1:2(新:老)
新生代用来存放新生的对象,由于频繁的创建对象,所以新生代会频繁的出现GC回收,采用复制算法进行垃圾回收
新生代分为三个区域 Eden , From survivor ,To servivor 默认比例 8:1:1
1:Eden(伊甸园区) java新对象的出身地(如果新创建的对象占用空间很大,则直接分配到老年代),当Eden区内存不够时会触发一次GC。存活的对象会被移动到from survivor区,
2:Servivor (幸存区):这个区域是存放eden区GC之后存活的对象,From和To这两个区域功能是轮流交换的,每次GC后对象年龄加1(默认到15的时候会到老年代),From区变To区,To变From区(谁是空的谁是To区)
3:From Survivor(From区):用来放Eden和To区GC后的存活对象,然后清空Eden和To区数据,在下次GC时变为To区
4:From Survivor(To区):用来放Eden和From区GC后的存活对象,然后情况Eden和From区数据,在下次GC时变为From区
老年代:主要用来存放生命周期长的对象,不会频繁的触发GC,只有新的大对象进入老年代找不到大的连续空间才会GC。采用标记清除算法
4: jvm的回收机制
1:什么是垃圾回收机制
垃圾回收机制简称GC, GC主要是在java堆中工作的。主要是不定时的去java堆中清理不可达对象
垃圾回收器是自动执行的,不能强制执行,程序员可以通过System.gc() 方法来建议执行垃圾回收。但是具体是否执行,什么时候执行都是不确定的。
2 : 为什么需要垃圾回收
如果不进行垃圾回收,内存迟早会被消耗完
3 : 垃圾识别机制(如何判断对象是否存活)
方法:引用计数算法,可达性分析算法,
-
引用计数算法:每个对象在创建的时候,就给这个对象绑定一个计数器,每次有地方引用这个对象时候,计数器就加1,当引用失效时计数器减1.。当计数器为0的时候表示没有被引用了,可以被回收了 。
但是在java中没有使用这种算法。因为无法解决对象之间相互循环引用的问题,会造成内存泄漏 -
可达性分析算法
以GC Roots 对象作为起点,从这个起点向下搜索引用的对象,搜索走过的路线称为引用链,找到的对象都标记为非垃圾对象,那么其余的对象则是不达对象,
4.1那些对象可以作为GC Roots对象?
1,虚拟机栈中引用的对象,比如方法中使用到的参数,局部变量等
2,方法区中类静态属性引用的对象,比如类中的静态变量
3,方法区中常量引用的对象,比如字符串常量池中的引用
4,本地方法栈引用的对象,比如native方法的对象
4.2 finalize()方法的使用
此方法是GC在回收对象之前调用的。
在可达性分析算法中被标记为不可达的对象,也不是一定会被回收 。它是判断这些对象是否有必要执行finalize()方法。
在finalize()方法中,对象有可能重新被利用,或者第二次被标记,然后对象被回收。
4.3垃圾回收机制
主要有四种方法:标记清除算法,标记整理算法,复制算法,分代收集算法
-
标记清除算法:算法分为“标记”和“清理”两个步骤。首先在标记阶段时,将所有活动对象的标记位设置为有效状态,表示这些对象是可达的,不会被回收。在清除阶段时,遍历整个内存空间,将未被标记的对象进行回收
缺陷 标记和清理的过程效率不高,容易产生大量不连续的内存碎片,后续再分配大对象时,无法找到足够的连续空间 -
标记整理算法:通过引用链将所有的活动对象设为有效。然后将这些有效的对象都向一端移动,然后直接清理另一端的内存空间
-
复制算法:将可用内存划分为大小相等的两块,每次只使用其中一块,当这一块内存使用完后,就将还存活的对象复制到另一块中,然后再把以使用的内存空间清理掉,算法执行效率高,适用于存活对象占少数的情况
缺陷:可用内存缩小了一般 -
分代收集算法:jvm采用的垃圾回收机制算法,根据对象生命周期的不同,将内存划分为几块,根据每块的特点采用不同的收集算法。