1.jvm类加载机制,每一步都做类什么:
加载(针对二进制字节流,把其转换成数据结构到内存) ,链接( 验证(验证元数据,文件格式),准备(正式为类的变量分配内存并进行初始赋值_),解析(将虚拟机的常量池的符号引用换成直接引用))_,初始化
2.jvm运行时数据区,垃圾回收算法,确定被清除对象:
程序计数器,java虚拟机栈,本地方法栈,方法区(共享),堆(共享)
tip:元空间的使用替代了永久代,永久代有内存溢出的风险,因为其依赖于虚拟机内存,而元空间依赖于本地内存
总结:
1、字符串存在永久代中,容易出现性能问题和内存溢出。
2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
4、Oracle 可能会将HotSpot 与 JRockit 合二为一。
回收算法:标记清除,标记整理,复制
GCROOT:
- 当前虚拟机栈中局部变量表中的引用的对象
- 方法区中类静态属性引用的对象
- 方法区中的常量引用的对象
3.垃圾回收器列举
Serial/Serial Old:最古老的,单线程,独占式,成熟,适合单 CPU 服务器 -XX:+UseSerialGC 新生代和老年代都用串行收集器
ParNew:比Serial的STW时间短,多线程,多 CPU 的
ParallelScavenge/Parallel Old:关注吞吐量的垃圾收集器,高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
CMS:收集器是一种以获取最短回收停顿时间为目标的收集器;四个阶段:初始标记,并发标记,重新标记,并发清除
优点:效率高,停顿时间短
缺点:
cpu资源敏感,并发清理阶段有浮动垃圾产生,使用标记-清除算法会有内存碎片问题。
G1: 把堆划分成多个大小相等的 独立区域(Region),新生代和老年代不再物理隔离
-
基于标记-整理算法, 不会产生空间碎片,分配大对象时不会无法得到连续的空间而提前触发一次full gc
-
停顿时间可控: G1可以通过设置预期停顿时间(Pause time)来控制垃圾收集时间,但是**这个预期停顿时间G1只能尽量做到,而不是一定能做到
**
G1 跟踪各个 Region 里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region(这也就是 Garbage-First 名称的来由)。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限的时间内可以获取尽可高的收集效率
回收过程:初始标记,并发标记,最终标记,筛选回收
相比于CMS适合于在大内存的应用使用,8g以上的应用使用效果比CMS一般更好,可预测的停顿也是一个优点
4.三色标记算法:
白色:未访问过
灰色:已访问到,但其引用对象还未访问完成
黑色:已访问到,其引用对象已经访问完成都不是垃圾
并发标记都会产生漏标记的问题:(引用在标记期间发生变化)
两个必要条件:
-
条件一:灰色对象 断开了 白色对象的引用;即灰色对象 原来成员变量的引用 发生了变化
-
条件二:黑色对象 重新引用了 该白色对象;即黑色对象 成员变量增加了 新的引用。
CMS:写屏障 + 增量更新 (记录新增引用维护起来,破坏条件2)
G1:写屏障 + SATB (引用被去掉断开后需要维护起来,破坏条件1)
ZGC:读屏障(只要有读取都会被记录下来)
5.MinorGC和FullGC发生时机:
Minor GC发生:当jvm无法为新的对象分配空间的时候就会发生Minor gc,所以分配对象的频率越高,也就越容易发生Minor gc
Full GC:发生GC有两种情况,①当老年代无法分配内存的时候,会导致MinorGC,②当发生Minor GC的时候可能触发Full GC,由于老年代要对年轻代进行担保,由于进行一次垃圾回收之前是无法确定有多少对象存活,因此老年代并不能清除自己要担保多少空间,因此采取采用动态估算的方法:也就是上一次回收发送时晋升到老年代的对象容量的平均值作为经验值,这样就会有一个问题,当发生一次Minor GC以后,存活的对象剧增(假设小对象),此时老年代并没有满,但是此时平均值增加了,会造成发生Full GC
6.FullGC产生常见原因:
- 应用中主动调用了
System.gc()
基本不可能是这个原因,首先没有人会闲得去调用这个方法,代码全局搜索后也印证了这个结果了;其次就算真的有人调用了这个System.gc()去建议JVM去支持FullGC,我们的服务在启动参数中开启了这个参数**-XX:ExplicitGCInvokesConcurrent**,这个参数的含义是将显示调用的**System.gc()**转为CMS的并发GC,所以并不会因此而触发FullGC(待重新理解) - 方法区(元空间)空间不足
这种一般的原因是代码中大量使用动态代理,生成了一大堆的代理类占用了方法区,如果是这个原因引起必然是所有服务都会报FullGC问题,然而其它机器的老年代内存很稳定,所以排查 - 直接内存空间不足
直接内存空间不足一般是用了nio这样的代码导致的 - 老年代空间不足
对象出生于新生代,在挺过了一次次minorGC之后成功熬到了老年代,并且持续在老年代混吃等死,一直到大量的对象都这样在老年代混吃等死把老年代占满之后就会触发FullGC