JVM知识学习
1. JVM的位置(jre中)
运行在操作系统之上(window,Linux,Mac…),操作系统运行在硬件体系之上。
2. JVM体系结构
注意:
- 栈,程序计数器不可能存在垃圾回收
- JVM调优大部分是在调堆
3.类加载器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVEfvfj4-1598964975996)(./类加载器.png)]
- 作用: 加载class文件
-
- 虚拟机自带的加载器
- 启动类(根)加载器 BootstrapClassLoader
- 扩展类加载器 ExtClassLoader
- 应用程序加载器 AppClassLoader
4.双亲委派机制(安全)
- 类加载器收到类加载的请求
- 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载;
- 启动加载器检查是否能够加载当前这个类,能够加载就结束,使用当前的类加载器,否则抛出异常,通知子加载器加载;
- 重复步骤3。
5.native
- 凡是带有native关键字的,说明java的作用范围达不到了,会去调用底层的C语言的库。 进入本地方法栈 ==> 调用本地方法接口 (Java Native Interface)
- JNI的作用: 扩展java的使用,融合不同的编程语言为java所用。
- 内存区域专门开辟了一块标识的区域: Native Method Stack 登记native的方法 。
- 在最终执行的时候,用JNI加载本地方法库。
6.PC寄存器(程序计数器)Program Counter Register
每一个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区的字节码,在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。
7.方法区 Method Area
方法区是被所有线程共享,此区域属于共享区间。
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但实例变量存在堆内存中,和方法区无关。
8.栈 stack
- 栈: 先进后出、后进先出。
队列:先进先出(FIFO: First Input First Output) - 栈内存,主管程序的运行,生命周期与线程同步;线程结束,栈内存也就是释放,对于栈来说,不存在垃圾回收
- 每个线程都包含一个栈区,保留基本数据类型,对象引用(reference),实例方法。
- 以栈帧为单位的压栈和出栈。
9.堆 Heap
- Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。
- 类加载读取类文件后,一般会把类、方法、常量、变量,存储在堆内存中。保存我们所引用类型的真实对象。
- 新生区:类诞生和生长的地方,甚至死亡。
- 伊甸园区:所有对象都是在伊甸园区new出来的。
- 永久存储区:这个区域常驻内存,用来存放JDK自身携带的Class的对象,Interface元数据,存储的是Java运行时的一些环境和常量,这个区域不存在垃圾回收!关闭JVM虚拟机就会释放这个区域的内存。
- 一个启动类,加载了大量的第三方的jar包、Tomcat部署了太多的应用、大量生成的反射类,不断动态生成的反射类,不断被加载,直到内存满,就会出先OOM
10.使用JProfile工具分析OOM原因
- 能够看到代码第几行错:内存快照分析工具,MAT,JProfiler
- MAT,JProfile作用:
- 分析Dump内存文件,快速定位内存泄漏
- 获得堆中的数据
- 获得大对象
- JVM参数
- -Xms:设置初始分配内存的大小 大约为总内存的1/64
- -Xmx:设置最大分配内存 大约为总内存的1/4
- –XX:+PrintGCDetails 打印GC垃圾回收信息
- –XX:+HeapDumpOnOutOfMemoryError 打印出OOM Dump
11.垃圾回收机制 GC
- GC分为Minor GC和 Full GC。Minor GC清除Eden和from,转到to中。接下来from和to转换。继续清除Eden和from区;Full GC 搜集整个堆,包括新生代,老年代,永久代(在JDK 1.8及以后,永久代会被移除,换为metaspace)等收集所有部分的模式。
- GC算法:标记清除法、标记整理法、复制算法、引用计数法。
- 复制算法:将内存容量划分为两个大小相等的内存块,每次只使用一个,当这个容量快用完了。就将存活的对象复制到另一个上面区,然后把已经使用过的内存空间全部清理。
- 使用场景:对象存活较低的内存空间,如Eden区。
- 优点:内存分配不用考虑碎片等复制情况。
- 缺点:空间利用率低;复制算法在对象存活率较高时就要进行较多的复制操作,效率会变低。
- 标记清除算法:算法分为标记和清除两个阶段:首先标记出需要被回收的对象,在标记完成后统一回收所有被标记的对象
- 使用场景:使用于老年代。因为老年代回收的几率小且不频繁能减少内存碎片。
- 缺点:标记和清除两个过程效率都不高;会产生大量不连续的内存碎片。
- 标记整理法:它的标记阶段和标记清除算法中的一样。整理阶段是将所有存活的对象压缩到内存的一端,最后清理边界外所有的空间。
- 总结:
- 效率:复制算法 > 标记整理算法 > 标记清除算法
- 内存利用率:标记整理算法 = 标记清除算法 > 复制算法
- 内存整齐度:复制算法 = 标记整理法 > 标记清除法
12.JMM Java Memory Model
- Java的并发采用的是共享内存模型
- 作用:缓存一致性协议,用于定义数据读写的规则。
- JMM对这八种指令的使用,制定了如下规则:
(1)不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write;
(2)不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存;
(3)不允许一个线程将没有assign的数据从工作内存同步回主内存;
(4)一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作;
(5)一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁;
(6)如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值;
(7)如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量;
(8)对一个变量进行unlock操作之前,必须把此变量同步回主内存。