新鲜出炉花了三天整理的JVM复习知识点面试突击必备

40次阅读

共计 2421 个字符,预计需要花费 7 分钟才能阅读完成。

此次 JVM 知识点包含以下几个部分

1. 类加载机制

2.jvm 运行时数据区

3.java 对象内存布局

4.jvm 内存模型

5. 垃圾回收机制

6. 垃圾收集器

7. 问题排查

一 类加载机制

主要说的部分是这一块

那么如何装载呢,这就谈到了咱们的双亲委派机制,简单来说就是类,向上递交,向下加载,源码和图如下

if (parent != null) {c = parent.loadClass(name, false);
  } else {
   // 由于 BootstrapClassLoader 是 C ++ 写的,在 java 中被视为 null
    c = findBootstrapClassOrNull(name);
  }

那么装载的过程是什么呢,如图

 

 

装载干了啥呢,大白话说就是一个你把一个 java 文件转换成二进制给 jvm 处理,官方一点的语言就是:

        ①通过一个类的全限定名获取这个类的二进制字节流。

        ②将这个字节流的静态存储结构转换为方法区的运行时数据结构。

        ③在堆中生成一个这个二进制字节流的 Class 对象作为访问入口。

那么链接呢,验证: 字面意思,准备: 赋默认值,解析: 符号引用转换为直接引用

二 运行时数据区

话不多说,看法宝,上图

方法区

存放:静态变量,常量,即时编译的 class 文件,类信息。
区别:1.8 之前叫 Perm Space 永久代,现在叫 Meta Space 元空间

 

虚拟机栈

 存放:栈帧;即方法的调用,-Xss 可以设置栈大小,默认 1M;使栈溢出的使递归。

构成:局部变量表;方法中定义的变量与方法的参数,

  操作数栈;压栈出栈存放数据的地方,

动态链接;这个的作用就是知道谁调用的,比方说 java 中的多态,最后会知道具体是哪个类,
方法返回地址;字面意思

存放:对象及数组

接下来咱们看一下这个指针指向问题

栈指向堆;   栈帧(方法中有一个变量)即 Object obj = new Object();
方法区指向堆;静态变量,private static Object obj = new Object();
堆指向方法区;由于方法区中存放的类信息,所以对于多态如何识别,这一点,就说明堆中有指向方法区的指针。

三 java 对象内存布局

四 jvm 内存模型

内存模型可以认为是运行时数据区的落地,那么当一个对象来的时候,如何分配内存空间呢

首先放入 Eden 区,看够不够,不够,minorGC,再试试 Survivor 是否足够,如果不够;
放入老年代看看够不够,不够就来一次 Full GC(minorGC+MajorGC);
如果还不够就 OOM 了。
那么对象进入老年代的条件是什么呢

新生代中年龄大于 15;
大对象(-XX:PretenureSizeThreshold 配置这个,大于这个数的就成为大对象)
动态年龄:即 survivor 区中 同一年龄的超过了该区一半,那么大于等于该年龄的对象直接进入老年代
minorGC,新生代放不下的时候
那么这里边放了这么对象,该如何回收呢

五 垃圾回收机制

什么是垃圾,如何确定垃圾呢

引用计数法

没有任何指针指向的就是垃圾,但是无法解决循环引用的问题

可达性分析

由 GCRoot(静态成员,Thread 线程,虚拟机栈的变量表,本地方法栈中的变量,类加载器,常量)作为头,向下顺藤摸瓜,能摸得到的就是好瓜,摸不到的就回收扔了。

既然已经确定了垃圾,那么如何回收呢

四种垃圾回收算法

标记 - 清除:将标记的清除掉,弊端就是内存不连续,容易产生内存碎片;
复制:内存分两块,将一端复制到另一端,解决了内存不连续,弊端就是内存有效区只由一半;
标记 - 整理:将垃圾回收后压缩整理一下,解决了内存有效区只有一半的问题;
分代算法:个人认为这个属于一种思想,即对前三种的一种总结;老年代用标记清除,标记整理,新生代用复制
算法说完了,那么算法的落地,如何实现的呢,这就要说到垃圾收集器了

六. 垃圾收集器

 

可以看到从刚开始的 Serial 到现在的 G1 乃至 ZGC 的最多 10ms 停顿可以看到 java 一直在寻找最短的停顿时间,这个也是一直优化的方向。

并行收集:多个线程一起收集

并发收集:跟用户线程一起跑

CMS 和 G1 的区别有哪些呢?

CMS,四个步骤为,初始标记 - 并发标记 - 重新标记 - 并发清理

G1,四个步骤,初始标记 - 并发标记 - 最终标记 - 筛选回收(对各个 Region 的回收价值进行排序根据用户期望的 GC 停顿时间制定回收计划)

G1 可以设置停顿时间(-XX:MaxGCPauseMillis=20), 就是因为他的 Region,可以理解为一面墙分成了多个砖头,一些砖头的集合称为老年代,一些称为新生代。

这些都知道了,那么出现错误该如何排查呢

七 问题排查

1. 频繁 FullGC

导致频繁 FullGC 的原因有

System.gc()
jmap -dump:format=b,fifile=heap.hprof PID

老年代内存不够
步骤

打印 FullGC 前后的日志 -XX:+HeapDumpBeforFullGC   -XX:+HeapDumpAfterFullGC   -XX:+HeapDumpPath=a.prof
使用 MAT 工具进行分析,可以看堆中占用情况,以及 class 的新建情况。
2. 线上 CPU 负载过高排查

采用 TOP 命令,查出占用 cpu 最高的 java 应用
top -Hp PID 查询出占用 cpu 最高的线程
找出该线程 ID,转换成 16 进制 printf “%xn”  tid
jstack PID > d.txt
打开 d.txt,查询该 16 进制的 tid 就能找到了
3. 吞吐量调优

使用命令打印出 gc.log   -XX:+PrintGCDetails  -Xloggc:gc.log
使用 gcviewer 来分析日志
根据具体情况调整堆栈大小,停顿时间等参数,再看 gcviewer 分析出来的数据如何。
4. 死锁排查

使用 java  bin 目录下自带的 visualVM 工具,可远程链接可本地链接
连接之后,点击线程一栏,便会出现红字  发现死锁
点击旁边的 Dump 按钮即可进入 dump 文件中,往下翻即可看到提示的哪一行出现了死锁,然后定位到代码

正文完
 0