【JVM】常见问题点摘取
文章目录
一、运行时数据区分类
JVM 运行时内存数据区分为: PC程序计数器、虚拟机栈、本地方法栈、堆空间、方法区。
PC程序计数器:用来存储指向下一条指令的地址,也即执行引擎要执行的指令代码。由执行引擎读取下一条指令。
虚拟机栈:栈是每个线程独有的,与线程的生命周期一致。内部保存着栈帧,代表一次次的方法调用。栈帧中存储着局部变量表、操作数栈、动态链接、方法返回地址、一些附加信息等。
动态链接:指向该方法在常量池里的引用
本地方法栈:本地方法是非Java的C语言方法,通过栈去访问本地方法库
堆空间:几乎所有的对象实例都存储在堆中。JDK 7 中将静态变量和字符串常量池优化到了堆中。堆又分年轻代、老年代。年轻代又分 Eden、survivor0、survival1区。
方法区:类型信息、域信息、方法信息、运行时常量池
字节码中的常量池:源代码编译后的class字节码指向常量池中的引用
二、类加载过程
1. 类加载过程
- Loading 加载
- 获取类的二进制字节流
- 将这个字节流存储结构转化为方法区的运行时数据结构
- 在内存中生成代表这个类的字节码对象
- Linking 链接
-
Verify验证(确保Class字节流中的信息是否符合当前虚拟机要求)
-
Prepare准备(为类变量分配内存并且为类变量初始值、不包含 static final 修饰的该修饰在编译器就分配了)
-
Resovler解析(将常量池内的符号引用转换为直接引用的过程)
- initialization初始化
- clinit(初始化类中的静态变量显式赋值、静态代码块)
- init
2. 类加载器有哪几种
- 引导类加载器 Bootstrap ClassLoader
- 用来加载Java的核心库
- 扩展类加载器 Extension ClassLoader
- 从JDK安装目录的jre/lib/ext子目录下加载类库
- 系统类加载器 AppClassLoader
- 负责加载环境变量classpath或系统属性 java.class.path 指定下的类库
- 程序默认的类加载器
- 通过ClassLoader#getSystemClassLoader() 方法可以获取该类加载器
3. 双亲委派机制
三、垃圾回收
1. 垃圾回收的算法
- 标记阶段:引用计数算法
- 标记阶段:可达性分析算法
- 清除阶段:标记-清除算法
- 清除阶段:复制算法
- 清除阶段:标记-压缩算法
- 分代收集算法
- 增量收集算法
- 分区算法
2. 垃圾回收器
- serial回收器:串行回收
- ParNew回收器:并行回收
- parallel回收器:并行回收
- CMS:低延迟
- G1:区域化分代式
简述G1
-
设计原则:在满足GC暂停时间的同时,提高吞吐量
-
JDK 9中的默认垃圾回收器
-
特点
- 并行与并发。并行指多个垃圾收集线程可以同时刻工作。并发指在整个垃圾回收过程,用户线程不会完全阻塞。
- 分代收集。
- 空间整合。使用复制算法和标记-压缩算法,减少碎片。
- 优先回收列表。G1会通过算法列出一个回收价值列表,在清除的时候会回收价值最大的
Region
3. Minor GC和Full GC触发条件
一、Minor GC触发条件
1、eden区满时,触发MinorGC。即申请一个对象时,发现eden区不够用,则触发一次MinorGC。
注:新生代分为三个区域,eden space, from space, to space。默认比例是8:1:1。在MinorGC时,会把存活的对象复制到to space区域,如果to space区域不够,则利用担保机制进入老年代区域。
二、Full GC触发条件
- 老年代空间、永久代空间(JDK 1.7 )不够分配新的内存
四、杂项
1. 方法区的存储内容与演变过程
https://www.cnblogs.com/cosmos-wong/p/12925299.html
2. 对象的内存结构
对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
3. 对象头的结构
HotSpot虚拟机的对象头包括两部分信息,
-
第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,官方称它为”Mark Word”。
-
对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据信息并不一定要经过对象本身。另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中却无法确定数组的大小。