【JVM】经典面试题总结-史上最全面试题思维导图总结
2022-10-22 13:39:45 17 举报
AI智能生成
https://github.com/JShengYi/CS_INTERVIEW 「Java学习+面试指南」思维导图,计算机自学指南,包括Java基础、JVM、数据库、mysql、redis、计算机网络、算法、数据结构、操作系统等,后台技术栈/架构师之路/全栈开发社区,阿里,腾讯,百度,美团,头条等春招/秋招/校招/面试
作者其他创作
大纲/内容
new
隐式装载
class.forname()
显式装载
类装载方式
Java_HOME/lib/
java核心类库
启动类加载器(Bootstrap ClassLoader)
\\lib\\ext
Java 的扩展库
扩展类加载器(extensions class loader)
类路径(CLASSPATH)
系统类加载器(system class loader)
继承 java.lang.ClassLoader类的方式
用户自定义类加载器
类加载器
首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成
所有的加载请求都会被传送到顶层的启动类加载器中
父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
双亲委派模型
jconsole
jvisualvm
-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
JVM 调优
类加载机制
尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收
长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露
内存泄漏
在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
由虚拟机自行执行
没有提供释放已分配内存的显示操作方法
垃圾回收线程
强引用:发生 gc 的时候不会被回收。
软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
弱引用:有用但不是必须的对象,在下一次GC时会被回收。
虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知。
引用类型
不能解决循环引用
引用计数器法
从 GC Roots 开始向下搜索
可达性分析算法
判断对象是否可以被回收
垃圾回收不会发生在永久代
如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)
Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区
永久代
缺点:效率不高,无法清除垃圾碎片。
标记-清除算法:标记无用对象,然后进行清除回收。
缺点:内存使用率不高,只有原来的一半。
复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。
标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
一般包括年轻代、老年代 和 永久代
一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
分代算法:根据对象存活周期的不同将内存划分为几块,
垃圾回收算法
单线程
Serial收集器(复制算法)
多线程
ParNew收集器 (复制算法):
追求高吞吐量,高效利用 CPU
Parallel Scavenge收集器 (复制算法)
新生代 复制效率高,缺点是内存利用率低
Serial收集器的老年代版本
Serial Old收集器 (标记-整理算法)
Parallel Scavenge收集器的老年代版本
Parallel Old收集器 (标记-整理算法)
高并发、低停顿,追求最短GC回收停顿时间
CMS(Concurrent Mark Sweep)收集器(标记-清除算法)
老年代 标记整理
整个Java堆(包括新生代,老年代)
G1(Garbage First)收集器 (标记-整理算法)
垃圾回收器
把 Eden + From Survivor 存活的对象放入 To Survivor 区;
清空 Eden 和 From Survivor 分区;
From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
Eden、To Survivor、From Survivor 8:1:1
复制算法
Minor GC
发生大量的内存复制
大对象直接进入老年代
当年龄达到一定程度(默认 15) 就会被晋升到老年代
长期存活对象将进入老年代
新生代1/3
标记整理
Major GC/Full GC
老年代2/3
分代垃圾回收器
垃圾回收
Class loader(类装载)
即时编译器
垃圾回收期
xecution engine(执行引擎)
两个子系统
静态变量放在方法区
静态的对象还是放在堆。
类信息、常量、静态变量、即时编译后的代码
方法区
几乎所有的对象实例都在这里分配内存
所有线程共享
堆
用于存储局部变量表、操作数栈、动态链接、方法出口等信息
虚拟机栈
为虚拟机调用 Native 方法服务
本地方法栈
所执行的字节码的行号指示器
程序计数器
栈
Runtime data area(运行时数据区)
Native Interface(本地接口)
两个组件
首先利用IDE集成开发工具编写Java源代码,源文件的后缀为.java;
再利用编译器(javac命令)将源代码编译成字节码文件,字节码文件的后缀名为.class;
运行字节码的工作是由解释器(java命令)来完成的。
类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能
主要组成
GC的时候也要考虑到不连续的分配
标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩)
物理地址分配对对象是不连续,性能慢
运行期确认的,因此大小不固定
实例和数组,数据的存储
先进后出的原则,物理地址分配是连续的。所以性能快
编译期就确认,大小是固定的
局部变量,操作数栈,返回结果,程序方法的执行
堆栈
使用new关键字\t调用了构造函数
使用Class的newInstance方法\t调用了构造函数
使用Constructor类的newInstance方法\t调用了构造函数
使用clone方法\t没有调用构造函数
使用反序列化\t没有调用构造函数
对象的创建
将位于中间的指针指示器向空闲的内存移动一段与对象大小相等的距离
规整,即所有用过的内存放在一边,而空闲的的放在另一边
指针碰撞
从列表中查询到足够大的内存分配给对象,并在分配后更新列表记录。
不是规整的,由虚拟机维护一个列表来记录那些内存是可用的
空闲列表
分配内存
CAS + 失败重试
同步处理
只有 TLAB 用完并分配新的 TLAB 时,才需要同步锁
每个线程在 Java 堆中预先分配一小块内存
本地线程分配缓冲
并发安全问题
引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而引用本身不需要修改
引用中存储对象的句柄地址句柄中包含了 对象实例数据 与 对象类型数据 各自的具体地址信息
句柄池
句柄访问
速度更快,节省了一次指针定位的时间开销。由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是非常可观的执行成本。
引用 中存储的直接就是对象地址,对象中存储类型数据
直接指针
对象的访问定位
基础
jvm
0 条评论
回复 删除
下一页