学习视频:https://www.bilibili.com/video/BV1iJ411d7jS?p=4&spm_id_from=pageDriver
1.JVM的位置
![](https://img2020.cnblogs.com/blog/1543695/202109/1543695-20210905205236072-1892756337.png)
2.JVM体系结构
3.双亲委派机制
A、说明:
当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
B、作用:
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
C、流程图(理解,向上委托,向下加载)
D、类加载器类别:(由上至下)
BootstrapClassLoader(启动类加载器)-> ExtClassLoader (标准扩展类加载器)-> AppClassLoader(系统类加载器)-> CustomClassLoader(用户自定义类加载器)
4.沙箱安全机制
Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。
所有的Java程序运行都可以指定沙箱,可以定制安全策略。
5.native关键字(主要用于方法上)
1、一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
2、在定义一个native方法时,并不提供实现体(比较像定义一个Java Interface),因为其实现体是由非Java语言在外面实现的
主要是因为JAVA无法对操作系统底层进行操作,但是可以通过JNI(java native interface java本地方法接口)调用其他语言来实现底层的访问。
举例:Thread类中的start() 方法中调用一个start0()的native方法。
6.方法区
1.放了些什么:每个类的结构信息(字段、方法数据、普通方法、构造方法),运行时常量池,静态变量内容。(这是规范,不同虚拟机的实现是不同的 最典型的就是永久代PermGen space和元空间Metaspace)
实例变量在堆内存中,和方法区无关。
2.绝对不是用来放方法的
3.这块区域所有线程共享,存在垃圾回收。
7.栈
栈:先进后出,后进先出
队列:先进先出(FIFO:First input First Output)
喝多了吐就是栈,吃多了拉就是队列
为什么main()先执行,最后结束
每个线程都有自己的栈,栈中的数据都是以栈帧的格式存在;在这个线程上正在执行的每一个方法都各自对应一个栈帧;栈帧是一个内存区块,是一个数据集维系着方法执行过程中的各种数据信息
栈:8大基本类型+对象引用+实例的方法()
栈是运行时的单位,Java 虚拟机栈,线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。(方法头开始入栈,结束出栈,方法里面调用别的方法 新的方法就会把旧的压在底下,最上面永远是正在执行的方法,也对应先入后出。)
局部变量表:存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)
8.堆
Heap,一个jvm只有一个堆内存,堆内存的大小是可以调节的
类加载器读取了类文件后,一般会把类、方法、常量、变量,保存我们所有引用类型的真实对象。
(下图为jdk8之前的,jdk8以后永久存储区的名字改为“元空间”)
永久区:这个区域常驻内存的。用来存放JDK自身携带的class对象,interface元数据,储存的是java运行时的一些环境或类信息,这个区域不存在垃圾回收,关闭VM虚拟机就会释放这个区域的内存
Jdk1.6之前:永久代,常量池在方法区
Jdk1.7:永久代,慢慢退化,去永久代,常量池在堆中
Jdk1.8之后:无永久代,常量池在元空间
什么时候出现永久区满
一个启动类,加载了大量的第三方jar包,tomcat部署了太多的应用,大量动态生成的反射类,不断的被加载,直到内存满,就会出现OOM
堆结构
发生OOM内存溢出,解决方法
A、 尝试扩大堆内存看结果
B、 分析内存,看一下哪个地方出现问题(专业工具)
1、内存溢出:(Out Of Memory—-OOM)
系统已经不能再分配出你所需要的空间,比如系统现在只有1G的空间,但是你偏偏要2个G空间,这就叫内存溢出
例子:一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出。比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出。说白了就是我承受不了那么多,那就报错。
2、内存泄漏: (Memory Leak)
强引用所指向的对象不会被回收,可能导致内存泄漏,虚拟机宁愿抛出OOM也不会去回收他指向的对象,意思就是你用资源的时候为他开辟了一段空间,当你用完时忘记释放资源了,这时内存还被占用着,一次没关系,但是内存泄漏次数多了就会导致内存溢出
3、JProfiler工具分析OOM原因:
分析Dump内存文件,快速定位内存泄漏
获得堆中的数据
获得大的对象
-XX:+HeapDumpOnOutOfMemoryError配置jvm参数,获取Heap Dump文件,用于分析结果
4、jvm调优
对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数。
JVM性能调优方法和步骤:
1.监控GC的状态;
2.生成堆的dump文件;
3.分析dump文件;
4.分析结果,判断是否需要优化;
5.调整GC类型和内存分配;
6.不断的分析和调整