对JVM的理解?
java8虚拟机有什么更新?
什么是oom?
什么是StackOverflowError?
有哪些方法分析?
JVM常用调优参数?
JVM中,对诶加载器的认识?
JVM体系概述
位置
JVM是运行在操作系统之上的,它与硬件 硬件没有直接的交互。
体系结构
数据区亮色区域线程共享,灰色线程不共享。
一、类装载器 ClassLoader
负责加载class文件,class文件内容在开头有特定的文件标识 ,将calss文件字节码内容加载到内存中,并将这些内容转化成方法区中的运行时数据结构并且ClassLoader 只负责class文件的加载,至于它是否可以运行,则由Excution Engine决定。
特定标识:
分类:
大致可分为四类:
(1)虚拟机自带的加载器
· 启动类加载器(根加载器) Bootstrap c++编写的
· 扩展类加载器 Extension java编写的
· 应用类加载器(AppClassLoader),加载当前应用的classpath的所有类。
(2)用户自定义加载器
· Java.lang.ClassLoader的子类,用户可以定制类的加载方式。
先来解释下启动类加载器,当程序中有new java自带的类的时候,加载这个文件时,启动类加载器会去加载;而当程序有new 我们自己写的类的时候,加载文件时,应用类加载器会去加载。
public class TEST {
public static void main(String[] args) {
// java自带的类
Object obj = new Object();
System.out.println(obj.getClass().getClassLoader());
// 自己写的类
TEST test = new TEST();
System.out.println(test.getClass().getClassLoader());
}
}
打印启动类加载器默认显示的就是null,而打印应用类加载器显示的就是AppClassLoader了
public class TEST {
public static void main(String[] args) {
TEST test = new TEST();
System.out.println(test.getClass().getClassLoader().getParent().getParent()); // 启动类加载器
System.out.println(test.getClass().getClassLoader().getParent()); // 扩展类加载器
System.out.println(test.getClass().getClassLoader()); // 应用类加载器
}
}
什么是ClassLoader的双亲委派机制?
java中为了保证类加载的安全,使用了双亲委派机制,优先从启动类加载器中加载,这个称为"父","父"无法加载到,再从扩展类加载器加载,这个称为 "母",“双亲委派”。如果都加载不到,那么会通过 "应用类加载器" 加载,如果也找不到会报:ClassNotFoundExcption。
二、本地接口
三、pc寄存器(程序计数器)
记录方法之间调用和执行情况,它用来指向下一条指令的地址。
四、方法区
所有线程共享
他存储了每一个类 的结构信息,例如:运行时常量池、字段、方法数据、构造方法和普通方法的字节码文件内容。上边讲的是规范,在不同虚拟机里头实现是不一样的,最典型的就是永久代和元空间。
栈管运行,堆管存储。
五、堆
六、java栈
后进先出,先进后出。
不存在垃圾回收。
栈也叫栈内存,主管java程序的运行,是在线程创建的时候创建,它的生命周期是跟随线程的生命周期,线程结束内存也就释放,对于栈来说,不存在垃圾回收的问题,只要线程一结束,就over,生命周期和线程一致,是线程私有的。8种基本类型的变量 + 对象的引用变量+实例方法都是在函数的栈内存种分配。
栈帧
等于java的方法,只不过在栈中方法为栈帧。
栈存储什么
栈帧中主要存储三类数据:
本地变量:输入和输出参数以及方法内的变量。
栈操作:记录出栈,入栈操作。
栈帧数据:包括类文件、方法等等,
栈运行原理
栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集。当一个方法A被调用时就产生了一个栈帧F1,并压入到栈中,A方法又调用了B方法,于是又产生了栈帧F2也被压入到栈,B方法又调用了C方法,于是产生了栈帧F3也被压入到栈中··· ···
执行完毕,先弹出F3栈帧,在弹出F1栈帧··· ···
遵循 “先进后出” "后进先出" 的原则。
每个方法执行的同时,都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法从调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程,栈的大小和具体JVM的实现有关,通常在256K~756K,约等于1M.
栈堆方法区关系
StackOverflowError 异常
栈溢出是一个错误。
堆
heap
OOM:堆内存溢出。
新生代(eden区伊甸区、from区幸存者0区、to幸存者1区)和老年代。
永久区
方法区:
原空间:
堆参数调整
一个16G的内存,默认分配给JVM 堆内存
最大大小为(-Xmx):物理内存的 "1/4" 也就是 16/4 (实际上16G内存只会有 15.4G大小),以15.4G为例,15.4 / 4 = 3.85G; 三个多G就是上限了。
初始值的默认大小为(-Xms):15.4 / 64 = 245M。
public class Lv20210814 {
public static void main(String[] args) {
// 返回Java虚拟机试图使用的最大内存值
long maxMemory = Runtime.getRuntime().maxMemory();
// 返回Java虚拟机中的内存总量
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("-Xmx MAX_MEMORY: "+maxMemory+"字节 -> "+(maxMemory / (double) 1024 / 1024) + "MB");
System.out.println("-Xms TOTAL_MEMORY: "+totalMemory+"字节 -> "+(totalMemory / (double) 1024 / 1024) + "MB");
}
}
在实际开发中,一般会调到一样大初始值和最大值,避免GC和应用程序争抢内存。
在idea中调整初始值和最大值
初始值和最大值都改为10Mb,测试大小自定义
-Xms10m -Xmx10m -XX:+PrintGCDetails
测试堆溢出
创建一个大于10Mb的对象即可
byte[] bytes = new byte[40*1024*1024];
对JVM GC的理解
采用分代收集算法,次数上频繁收集Young区。此时上较少收集Old区,基本不动元空间。
未完成,待续。