整理的超硬核JVM笔记分享
写在前面
最近,一直有小伙伴让我整理下关于JVM的知识,经过十几天的收集与整理,初版算是整理出来了。希望对大家有所帮助。
记得点赞收藏加关注哦 ,需要下载PDF版本和更多知识点、面试题的朋友可以点一点下方链接免费领取
链接:点这里!!! 799215493 暗号:CSDN
JDK 是什么?
JDK 是用于支持 Java 程序开发的最小环境。
- Java 程序设计语言
- Java 虚拟机
- Java API类库
JRE 是什么?
JRE 是支持 Java 程序运行的标准环境。
- Java SE API 子集
- Java 虚拟机
Java历史版本的特性?
Java Version SE 5.0
- 引入泛型;
- 增强循环,可以使用迭代方式;
- 自动装箱与自动拆箱;
- 类型安全的枚举;
- 可变参数;
- 静态引入;
- 元数据(注解);
- 引入Instrumentation。
Java Version SE 6
- 支持脚本语言;
- 引入JDBC 4.0 API;
- 引入Java Compiler API;
- 可插拔注解;
- 增加对Native PKI(Public Key Infrastructure)、Java GSS(Generic Security Service)、Kerberos和LDAP(Lightweight Directory Access Protocol)的支持;
- 继承Web Services;
- 做了很多优化。
Java Version SE 7
- switch语句块中允许以字符串作为分支条件;
- 在创建泛型对象时应用类型推断;
- 在一个语句块中捕获多种异常;
- 支持动态语言;
- 支持try-with-resources;
- 引入Java NIO.2开发包;
- 数值类型可以用2进制字符串表示,并且可以在字符串表示中添加下划线;
- 钻石型语法;
- null值的自动处理。
Java 8
- 函数式接口
- Lambda表达式
- Stream API
- 接口的增强
- 时间日期增强API
- 重复注解与类型注解
- 默认方法与静态方法
- Optional 容器类
运行时数据区域包括哪些?
- 程序计数器
- Java 虚拟机栈
- 本地方法栈
- Java 堆
- 方法区
- 运行时常量池
- 直接内存
程序计数器(线程私有)
程序计数器(Program Counter Register)是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。
由于 Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式实现的。为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各线程之间的计数器互不影响,独立存储。
- 如果线程正在执行的是一个 Java 方法,计数器记录的是正在执行的虚拟机字节码指令的地址;
- 如果正在执行的是 Native 方法,这个计数器的值为空。
程序计数器是唯一一个没有规定任何 OutOfMemoryError 的区域。
Java 虚拟机栈(线程私有)
Java 虚拟机栈(Java Virtual Machine Stacks)是线程私有的,生命周期与线程相同。
虚拟机栈描述的是 Java 方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧(Stack Frame),存储
- 局部变量表
- 操作栈
- 动态链接
- 方法出口
每一个方法被调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
这个区域有两种异常情况:
- *Error:线程请求的栈深度大于虚拟机所允许的深度
- OutOfMemoryError:虚拟机栈扩展到无法申请足够的内存时
本地方法栈(线程私有)
虚拟机栈为虚拟机执行 Java 方法(字节码)服务。
本地方法栈(Native Method Stacks)为虚拟机使用到的 Native 方法服务。
Java 堆(线程共享)
Java 堆(Java Heap)是 Java 虚拟机中内存最大的一块。Java 堆在虚拟机启动时创建,被所有线程共享。
作用:存放对象实例。垃圾收集器主要管理的就是 Java 堆。Java 堆在物理上可以不连续,只要逻辑上连续即可。
方法区(线程共享)
方法区(Method Area)被所有线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
和 Java 堆一样,不需要连续的内存,可以选择固定的大小,更可以选择不实现垃圾收集。
运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。保存 Class 文件中的符号引用、翻译出来的直接引用。运行时常量池可以在运行期间将新的常量放入池中。
如何判断对象是否“死去”?
- 引用计数法
- 根搜索算法
什么是引用计数法?
给对象添加一个引用计数器,每当有一个地方引用它,计数器就+1,;当引用失效时,计数器就-1;任何时刻计数器都为0的对象就是不能再被使用的。
引用计数法的缺点?
很难解决对象之间的循环引用问题。
Java 的4种引用方式?
在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为
- 强引用 Strong Reference
- 软引用 Soft Reference
- 弱引用 Weak Reference
- 虚引用 Phantom Reference
强引用
Object obj = new Object();
代码中普遍存在的,像上述的引用。只要强引用还在,垃圾收集器永远不会回收掉被引用的对象。
软引用
用来描述一些还有用,但并非必须的对象。软引用所关联的对象,有在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围,并进行第二次回收。如果这次回收还是没有足够的内存,才会抛出内存异常。提供了 SoftReference 类实现软引用。
弱引用
描述非必须的对象,强度比软引用更弱一些,被弱引用关联的对象,只能生存到下一次垃圾收集发生前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。提供了 WeakReference 类来实现弱引用。
虚引用
一个对象是否有虚引用,完全不会对其生存时间够成影响,也无法通过虚引用来取得一个对象实例。为一个对象关联虚引用的唯一目的,就是希望在这个对象被收集器回收时,收到一个系统通知。提供了 PhantomReference 类来实现虚引用。
有哪些垃圾收集算法?
- 标记-清除算法
- 复制算法
- 标记-整理算法
- 分代收集算法
分代收集算法
根据对象的存活周期,将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点,采用最适当的收集算法。
- 新生代:每次垃圾收集时会有大批对象死去,只有少量存活,所以选择复制算法,只需要少量存活对象的复制成本就可以完成收集。
- 老年代:对象存活率高、没有额外空间对它进行分配担保,必须使用“标记-清理”或“标记-整理”算法进行回收。
记得点赞收藏加关注哦 ,需要下载PDF版本和更多知识点、面试题的朋友可以点一点下方链接免费领取
链接:点这里!!! 799215493 暗号:CSDN
Minor GC 和 Full GC有什么区别?
Minor GC:新生代 GC,指发生在新生代的垃圾收集动作,因为 Java 对象大多死亡频繁,所以 Minor GC 非常频繁,一般回收速度较快。
Full GC:老年代 GC,也叫 Major GC,速度一般比 Minor GC 慢 10 倍以上。
Java 内存
为什么要将堆内存分区?
对于一个大型的系统,当创建的对象及方法变量比较多时,即堆内存中的对象比较多,如果逐一分析对象是否该回收,效率很低。分区是为了进行模块化管理,管理不同的对象及变量,以提高 JVM 的执行效率。
堆内存分为哪几块?
- Young Generation Space 新生区(也称新生代)
- Tenure Generation Space养老区(也称旧生代)
- Permanent Space 永久存储区
分代收集算法
内存分配有哪些原则?
- 对象优先分配在 Eden
- 大对象直接进入老年代
- 长期存活的对象将进入老年代
- 动态对象年龄判定
- 空间分配担保
Young Generation Space (采用复制算法)
主要用来存储新创建的对象,内存较小,垃圾回收频繁。这个区又分为三个区域:一个 Eden Space 和两个 Survivor Space。
- 当对象在堆创建时,将进入年轻代的Eden Space。
- 垃圾回收器进行垃圾回收时,扫描Eden Space和A Suvivor Space,如果对象仍然存活,则复制到B Suvivor Space,如果B Suvivor Space已经满,则复制 Old Gen
- 扫描A Suvivor Space时,如果对象已经经过了几次的扫描仍然存活,JVM认为其为一个Old对象,则将其移到Old Gen。
- 扫描完毕后,JVM将Eden Space和A Suvivor Space清空,然后交换A和B的角色(即下次垃圾回收时会扫描Eden Space和B Suvivor Space。
Tenure Generation Space(采用标记-整理算法)
主要用来存储长时间被引用的对象。它里面存放的是经过几次在 Young Generation Space 进行扫描判断过仍存活的对象,内存较大,垃圾回收频率较小。
Permanent Space
存储不变的类定义、字节码和常量等。
类加载器
类加载器的作用是什么?
类加载器实现类的加载动作,同时用于确定一个类。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。即使两个类来源于同一个Class文件,只要加载它们的类加载器不同,这两个类就不相等。
类加载器有哪些?
- 启动类加载器(Bootstrap ClassLoader):使用C++实现(仅限于HotSpot),是虚拟机自身的一部分。负责将存放在\lib目录中的类库加载到虚拟机中。其无法被Java程序直接引用。
- 扩展类加载器(Extention ClassLoader)由ExtClassLoader实现,负责加载\lib\ext目录中的所有类库,开发者可以直接使用。
- 应用程序类加载器(Application ClassLoader):由APPClassLoader实现。负责加载用户类路径(ClassPath)上所指定的类库。
类加载机制
什么是双亲委派模型?
双亲委派模型(Parents Delegation Model)要求除了顶层的启动类加载器外,其余加载器都应当有自己的父类加载器。类加载器之间的父子关系,通过组合关系复用。
工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有到父加载器反馈自己无法完成这个加载请求(它的搜索范围没有找到所需的类)时,子加载器才会尝试自己去加载。
为什么要使用双亲委派模型,组织类加载器之间的关系?
Java类随着它的类加载器一起具备了一种带优先级的层次关系。比如java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各个类加载器环境中,都是同一个类。
如果没有使用双亲委派模型,让各个类加载器自己去加载,那么Java类型体系中最基础的行为也得不到保障,应用程序会变得一片混乱。
什么是类加载机制?
Class文件描述的各种信息,都需要加载到虚拟机后才能运行。虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
虚拟机和物理机的区别是什么?
这两种机器都有代码执行的能力,但是:
- 物理机的执行引擎是直接建立在处理器、硬件、指令集和操作系统层面的。
- 虚拟机的执行引擎是自己实现的,因此可以自行制定指令集和执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令集格式。
Java 方法调用
什么是方法调用?
方法调用唯一的任务是确定被调用方法的版本(调用哪个方法),暂时还不涉及方法内部的具体运行过程。
Java的方法调用,有什么特殊之处?
Class文件的编译过程不包含传统编译的连接步骤,一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址。这使得Java有强大的动态扩展能力,但使Java方法的调用过程变得相对复杂,需要在类加载期间甚至到运行时才能确定目标方法的直接引用。
Java虚拟机调用字节码指令有哪些?
- invokestatic:调用静态方法
- invokespecial:调用实例构造器方法、私有方法和父类方法
- invokevirtual:调用所有的虚方法
- invokeinterface:调用接口方法
虚拟机是如何执行方法里面的字节码指令的?
解释执行(通过解释器执行)
编译执行(通过即时编译器产生本地代码)
解释执行
当主流的虚拟机中都包含了即时编译器后,Class文件中的代码到底会被解释执行还是编译执行,只有虚拟机自己才能准确判断。
Javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程。因为这一动作是在Java虚拟机之外进行的,而解释器在虚拟机的内部,所以Java程序的编译是半独立的实现。
最后
由于篇幅有限,这里只展示一部分,需要完整版的朋友可以点一点下方链接免费领取~
在这里也为大家整理了各个知识点模块整理文档(微服务、数据库、mysql、jvm、Redis等都有)和更多大厂面试真题,有需要的朋友可以点一点下方链接免费领取
链接:点这里!!! 799215493 暗号:CSDN
本文地址:https://blog.csdn.net/XingXing_Java/article/details/109966182
上一篇: 正则表达式 \w \d 的意义
下一篇: Redis的配置、启动、操作和关闭方法