JVM知识整理

前言

与计网的那篇一样,整理我看过的JVM知识。我刷完了B站的视频,配合《深入理解Java虚拟机》使用。最有意思是书中的实战部分,内存结构的时候,触发不同内存区域的OOM!没系统整理时,别问,一问就GG。哈哈,每天尽力更新整理,攒面试人品了!

思维导图


内容太多了,先不展开导图

Java内存区域

导图

内存三大块:

1. 堆

分为年轻代(Eden、From Survivor、To Survivor,大小比例8:1:1)、老年代。
分区原因具体涉及到__GC__

存放内容

对象实例
数组

转化过程:

新对象在eden区分配(内存不够会在老年代分配),一次GC存活则进入s0、s1中,存活15代进入老年代

异常抛出

OOM

2. 方法区(Non-heap)

又称永久代

存放内容:

类信息
常量
静态变量
JIT编译代码
PS:JDK8移除了方法区,把这些数据放到了直接内存的元数据区
常量池信息,存放编译器生成的字面量和符号引用,JDK7移动到堆上存储

异常抛出:

OOM

3. 栈

分为虚拟机栈、本地方法栈。
64位长度long和double占用两个局部变量空间(Slot)其他的占用1个

存放内容
  1. 局部变量表(编译期完成,对应基本数据类型、对象引用)
  2. 操作数栈(字节码相关)
  3. 动态链接
  4. 方法出口
  5. 常量引用
  6. 小对象(无逃逸时,自动回收)
异常

OOM(无法动态扩展时)
SOF(大于允许深度时)

4. 程序计数器

存放当前线程字节码指令地址,无OOM抛出

5. 直接内存

1. NIO

基于通道(channel)和缓存区(buffer)
直接使用Native函数分配堆外内存(__DirectByteBuffer__对象引用这块内存)

  1. 避免Java堆、Native堆来回复制数据
  2. 不受Java堆限制(但受本机总内存限制)

垃圾回收

算法分类

  1. 引用计数法
    原理:维护对象的引用计数,为0时回收
    问题:

    1. 性能开销
    2. 循环引用问题
  2. 标记-清除法
    原理:从根节点出发,标记所有可达对象,把不可达对象删除

  3. 标记-压缩法
    原理:与标记-清除法相同,增加了移动到一侧,清空边界以外的点。以消除碎片

  4. 复制算法

    原理:两块相同大小的空间,每次把存活对象放到另一块,清空本块。并把大对象放到老年代。
    问题:

    1. 浪费一半空间
    2. 不适合存活过多的内存区域(老年代)

分代思想

少量对象,适合复制算法
大量存活,适合标记清理、压缩

一些概念

  1. 什么是可触及性?
    以下是可触及的:
    1. 栈中的引用对象
    2. 方法区的静态成员
    3. 常量的引用
    4. JNI方法栈中的引用对象
  2. 什么时候可复活?
    finalize()中复活对象(当然只会复活一次)
  3. 为什么避免使用finalize?
    1. 无法确定GC的时间
    2. 为什么不使用更确定性的try catch finally呢?
  4. 什么是Stop-The-World现象?
    多半是由GC引起的出现全局暂停的现象,(其他原因:Dump线程,死锁检查,堆Dump)
  5. Stop-The-World有什么危害?
    1. Java长时间无响应(老年代满出现这种情况)
    2. 考虑有集群的时候,会误判宕机,而出现主备切换(扯远了)

术语

name 别名 含义
FUll GC Major GC 清理整个堆
Old GC 清理老年代内存
Mxinor GC Young GC 清理年轻代内存

具体算法

串行收集器(-XX:UseSerialGC)

毫无疑问,串行的效率最高(回收垃圾的效率),但是带来了较长的停顿。使用该参数则表示:
1. 新生代、老年代使用串行回收
2. 新生代使用复制算法
3. 老年代使用标记-压缩

并行收集器(-XX: UseParNewGC)

复制算法的实现,多线程并行复制,当然是在新生代使用。

并行收集器(-XX: UseParallelGC,-XX: UseParallelOldGC)

新生代使用复制算法,老年代使用标记-压缩(默认不并行,使用后面的才并行)

CMS(Concurrent Mark Sweep)并发标记清除算法

这个就要详细说说了。是标记-清除算法的并发实现。
过程如下:
1. 初始标记(串行,暂停用户线程)
由根直接能标记到的对象,速度快
2. 并发标记(与用户线程并行)
标记全部可达对象
3. 重新标记(串行)
暂停整个应用程序,修正标记,准备清理了
4. 并发清理(与用户线程并行)
清理垃圾
5. 并发重置
开始下一个循环
特点:
1. 停顿时间短,多次与用户线程并行
2. 并发时,系统吞吐量降低(GC线程与应用线程一起工作,应用吞土量当然降低了)
3. 清理不彻底(清理时与用户线程并行,期间产生的垃圾只能下次GC回收)
使用注意事项:
1. 老年代使用(对象变化较小,重新标记、清理期间做的工作更加有效)
2. 不能在空间快满的时候清理,会出现Concurrent mod failure错误 。此时会使用串行收集器,得不偿失。
-XX:CMSInitiatingOccupancyFraction 设置触发GC的阈值
几个参数:
-XX: UseConcMarkSweepGC(使用CMS)
-XX: UseCMSCompactAtFullCollection(Full GC后进行碎片整理)
-XX: CMSFullGCBeforeCompaction(进行几次GC后再进行碎片整理)
-XX:ParallelCMSThreads(CMS线程数量)

如何减轻GC压力??

  1. 软件架构的设计
  2. 代码编写
  3. 堆如何分配
Author: whllhw
Link: https://whllhw.ml/posts/2019/04/15/JVM知识整理/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.