深入理解JVM之(内存管理)

JVM内存区域

JVM内存总体概况
在这里插入图片描述

程序计划器
程序计数器是较小的一块内存区域,可以看做java执行的行号,本地方法计数器值为空,每个线程都会有一个独立的程序计数器,在线程挂起时候会被保存起来以便下次执行时候使用。唯一没有OOM异常的区域,是线程私有的数据区。

java虚拟机栈
线程私有的区域,生命周期与线程相同,每个方法允许的时候都会创建一个栈帧(Stack Frame)用于存储局域变量表、操作数栈、动态连接、方法出口等信息,每个方法的调用都对应一个栈帧出栈和入栈的过程。
局部变量表存放了编译器可知的基本数据类型(boolean、byte、char等)、对象引用(reference指向对象原始地址的引用指针)等

本地方法栈
与虚拟机栈租用相似,区别是虚拟机栈是为java方法服务,本地方法栈是为本地native方法服务。本地方法栈也会有栈深度移除或栈扩展失败抛出stackOverflowError和OutOfMemoryError等异常。

java堆
java堆heap是虚拟机所管理的内存最大的一块。是被所有线程共享的一块内存区域,目的就是为了存放对象,TLAB线程私有的分配缓冲区。堆无法为对象分片空间时,会抛出OOM异常。

方法区(元空间)
方法区与java堆一样,是各个线程贡献的内存区域,存放一杯虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码缓存等数据,1.8之后元空间使用本地内存,其大小只会受到操作系统最大可用内存的限制。

对象的创建

  1. new对象的时候,JVM会首先去方法区寻找该对象的类信息,没有则使用类加载器Classloader加载字节码文件
  2. 在JVM堆中分配内存,会通过TLAB技术,分配一块线程私有的内存区域,如果不够用则会继续分配。内存分配方式主要是指针碰撞、空闲列表两种方式。
  3. JVM进程运行中同事也创建了一个虚拟机栈,虚拟机栈用来跟踪线程运行中一些列方法调用
  4. JVM GC

对象的内存布局

对象的内存主要有三部分:

-对象头 header
对象头主要有两类信息:
存储对象自身的运行时数据,如哈希code、GC分代年龄、锁状态、线程持有的锁、偏向锁的ID、偏向时间戳等,官方成为Mak Word,动态数据区域,对象的状态复用自己的空间,长度为32位或64位
对象头头的另一部分是类型指针,即对象指向他的类型元数据的指针,java虚拟机通过这个指针确定该对象是哪个类的实例。
此外,对象如果是java数组,则元数据信息确定java对象的大小,

- 实例数据 Instance Data
实例数据部分是对象真正存储的有效信息。

- 对齐填充 Padding
对象的大小必须是8字节的整数倍,不够就需要补齐。

对象的访问定位

访问方式主要有两种:

使用句柄
使用句柄访问的话java堆中可能会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据和类型数据各自的地址。

直接访问
java堆中对象的内存布局就必须考虑如何防止访问类型数据的相关信息,reference中存储的就是对象的地址,如果访问对象本身就不需要再访问一次了。
在这里插入图片描述

Hotspot虚拟机主要使用第二种方式访问。

垃圾收集器

判断对象是否可回收

引用计数器

对象中添加一个引用计数器,有一个地方引用就+1,失效就-1,计数器为0就是可回收的。但是单纯的引用计数器很难解决对象之间循环引用的问题。java没有采用这种方式。

可达性分析

通过一些列的GC Roots 的根节点对象作为起始节点集,如果和根节点有关联则存活,没有则可回收。
在这里插入图片描述
GC Roots种类:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象,参数、局部变量、临时变量。
  • 方法区中类静态属性应用的对象,java类中引用类型静态变量
  • 方法区中常量应用的对象、比如字符串常量池里的引用
  • 本地方法栈中JNI引用的对象
  • java虚拟机内部的引用,比基本数据类型对应的Class对象,一些长度的异常对象,系统类加载器
  • 所有被同步锁(synchronized关键字)持有的对象

总结看来,GC Roots也是为了确定对象是否存活的方式,根节点的类型主要是为了当前正在运行中的线程(活着的线程)使用的对象,所以根节点都是线程私有的数据对堆的关联的数据。

引用的类型

  1. 强引用 Strongly Reference

在GC时候不会被回收
通过引用赋值 Object c = new Object();

  1. 软引用 Soft Reference
    在GC回收的时候内存不充足就会被回收。
Long[] longs = new Long[1024 * 1024 * 1024*1024*1024*1024];
        SoftReference<Long[]> userSoftReference = new SoftReference<>(longs);
        Long[] longs1 = userSoftReference.get();
        System.out.println(longs1);
  1. 弱引用 Weak Reference
    弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:
    在这里插入图片描述

  2. 虚引用 Phantom Reference
    寻引用会在被回收的时候加入引用队列然后消失,用来通知对象被回收了,具体使用看下图。
    在这里插入图片描述

垃圾收集算法

分代收集算法
java对划分Wie新生代和老年两个区域,跨代引用较少、大部分的对象会被立刻回收、代越多的对象越难被收回。
标记-清除算法

统一标记,统一回收,会有碎片区域。

标记-复制算法

标记存活的对象,然后整体换个位置,把剩下的全部回收调。减小碎片、占用空间交大。主要是因为大部分对象都是朝生夕死的,应用于年轻代的垃圾回收。

标记整理

在标记清除之后,再对没有回收的对象进行一次碎片整理。

Hotspot的算法实现细节
根节点枚举

垃圾回收进行根节点枚举的时候,必须STW,比如CMS、G1、ZGC也都是需要停顿的,通过OOpMap数据结构来进行根节点枚举,根节点枚举的速度很快停顿时间很短。

安全点

线程正在执行的过程中进行垃圾回收,线程必须快速到达安全点进行暂停,抢先式中断、主动式中断。
Polling:JVM通过轮询方式检查是否需要进行垃圾收集。这种方式比较简单,但会带来一定的性能开销。

Page fault:JVM利用操作系统的页面故障机制,在堆内存的某个位置设置一个非法地址,当执行线程访问到这个地址时,会触发页面故障,从而使JVM停止应用程序的执行。

Thread suspension:JVM通过暂停执行线程的方式来实现安全点。这种方式可以保证所有执行线程都处于安全状态,但会对应用程序的响应时间产生影响。

安全区域

安全区域简单就是说就是某一段代码之中,引用关系不会发生变化的,因此,这个区域的任意开始垃圾垃圾回收都是安全的。离开安全局域需要等待垃圾回收结束。

记忆集和卡表

为了解决对象跨代引用问题,垃圾收集器在新生代建立了名为记忆集的数据结构,垃圾回收的时候只需要遍历记忆集,而不需要去遍历老年代。卡表是标记记忆集中的是指向回收区域的指针

并发的可达性分析

三色标记算法:
白色 表示对象尚未被垃圾收集器访问过,可达性分析刚开始阶段,所有对象都是白色的,若是分析结束,白色的对象就是垃圾。
黑色 表示对象已经被垃圾收集器访问过,黑色是存活的对象
灰色 表示对象已经被垃圾收集器访问过,但是对象上至少有一个引用没有被垃圾收集器访问过。

CMS收集器

CMS收集器是一种以获取最短回收停顿时间为目标的收集器,CMS收集器是基于标记-清除算法实现的,他的运作过程中对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤:

  1. 初始标记
  2. 并发标记
  3. 重新标记
  4. 并发清除
    在这里插入图片描述
Garbage Fist 收集器 (G1垃圾收集器)

G1是主要面向服务端应用的垃圾收集器,目标是替换CMS收集器。
G1基于Region的堆内存布局,G1不适用固定大小以及固定数量的分代区域划分,而是把连续的java堆划分为多个大小相等的独立区域(region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理。
Region中的Humongous区域,专门用来存储大对象,G1认为只要大小超过了一个Region的一般的对象就是大对象,当做老年代来看待。
虽然G1仍然保留老年代和新生代的概念,但是新生代和老年代不再是固定的了,他们都是一些列区域的动态集合。
使用region有效的避免了对整个java堆进行全区域的垃圾回收,监控全区域的region维护一个回收优先级的列表,然后根据垃圾回收允许停顿的时间进行回收。
在这里插入图片描述

G1垃圾回收的步骤

  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 筛选回收

G1除了并发标记阶段外,其余阶段也是要完全暂停用户线程的,

G1和CMS的区别

G1 可以指定最大停顿时间,分Region的内存布局、按受益动态确定回收集,G1整体是基于标记-复制,G1在运作期间不会产生内存空间碎片。G1在垃圾回收期间CPU、内存占用都更高。

Shenandoah

Shenandoah是开源的OpenJdk的才会有的垃圾收集器,RedHat公司开发的。
Shenandoah也是基于Region的堆内存布局,也有大对象的Humongous、以及基于收益的回收优先级列表。
垃圾回收的过程有九个阶段:

  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 并发清理
  5. 并发回收
  6. 初始引用更新
  7. 并发引用更新
  8. 最终引用更新
  9. 并发清理
ZGC收集器

ZGC和Shenandoah 的目标是高度相似的。都希望在尽可能对吞吐量影响不太大的前提下,实现对任意堆内存大小下都可以把垃圾收集器的停顿时间限制在十毫秒,以内的低延迟。ZGC也是基于Region的堆内存布局的。分为小型、中型、大型的region。
ZGC的核心并发整理算法,Shenandoah使用转发指针和读屏障,ZGC虽然同样使用到了读屏障,但是与Shenandoah不同。
ZGC收集器有一个标志性的设计是它采用的染色指针技术,如果我们要在对象上存储一些额外的、只供收集器或者虚拟机本身使用的数据,通常会在对象头中增加外的存储字段,如果对象的哈希码、分代年龄、锁记录等就是这样存储的。这帐篷那个记录方式在有对象访问的场景下可以,如果对象存在被移动过的可能性。
在追中式收集算法的标记阶段就可能存在只跟指针打交道而不必设计指针所引用的对象本身的场景。例如对象标记的过程中需要给对象打上三色标记,这些标记本质上就纸盒对象的引用有关,而对象本身无关-某个对象只有它的引用关系能决定它存活与否,对象上其他所有的属性都不能影响它的存活判断结果。
HotSpot虚拟机的集中收集器有不同的标记实现方案,有的吧标记直接记录在对象头上(如Serial收集器),有的把标记记录在于对象互相独立的数据结构上(G1、Shenandoah使用一个中相当于堆内存的1/64大小的,称为bitmapo的结构来记录标记信息),而ZGC的染色指针是最直接、最纯粹的,他直接把标记信息记录在应用对象的指针上,
染色指针是一种直接将少量额外的想信息存储在指针上的技术,Linux的64位指针的高18位不能用来寻址,但是剩余的46位指针所能支持的64TB内存,,ZGC的染色指针技术就是使用剩下的46位指针宽度,将其高4位提出出来存储四个标志信息,通过这些标志为,虚拟机可以直接从指针中看到其引用对象的三色标记状态、是否进入重分配集(即被移动过)、是否只能通过finalize方法才能被访问到,
在这里插入图片描述
虽然染色指针的有4TB的内存限制,不能支持32位平台,不能支持压缩指针。
优势:

  • 染色指针可以使得一旦某个Region的存活对象被移走之后,这个Region立即就能被释放和重用掉,而不必等待整个 对重所有指向该Region的引用被被修正之后次啊能清理。理论上只要有一个空闲的Region,ZGC就能完成收集,
  • 染色指针可以大幅减少在垃圾回收过程中内存屏障的使用数量。设置内存屏障,尤其是写屏障的目的通常是为了记录对象引用的变动情况,如果将这些信息直接维护到指针中,显然就可以剩余一些专门的记录操作,实际上,到目前为止ZGC都并未使用任何写屏障,只用使用了读屏障,ZGC没有分代概念,没有跨待引用的问题。效率更高
  • 染色指针可以作为一种可扩展的存储结构用来记录更多的对象标记、重定位过程相关的数据,一遍日后进一步提高性能。

应用染色指针,还有一个一个问题就是操作系统是否支持在指针中添加数据,ZGC的解决方案是虚拟内存映射技术。在linux/x
86-64平台上的ZGC使用了多重映射将逗哥不同的虚拟内存地址映射到同一个物理内存地址上,这是一种多对一的映射,意味着ZGC在虚内存中看到的地址空寂那要比实际的堆内存容量来的更大,把染色指针中的标志看做是地址的分段符,只要将这些不同的地址端都映射到同一个物理内存空间,经过多重映射转换后,就可以使用染色指针了。

ZGC的垃圾收集流程

  1. 并发标记
    并发标记是遍历对象图做(尽管ZGC中的名字不叫做这些)的短暂停顿,而且这些停顿阶段所做的事情在目标上也是类似的,与G1、Shenandoah不同的是,ZGC的标记是在指针上而不是在对象进行的,标记阶段会更新染色指针中的Marked 0、Marked 1标记位1

  2. 并发预备重分配
    这个阶段需要跟姐姐特定的查询条件统计得出本次手机过程要清理那些Region,将这些Region组成重分配集合,重分配集合与G1手机器的回收集是有区别的,ZGC划分Region的目的并非为了想G1那样做收益首先的增量回收,相反,ZGC每次回收都会扫描所有的Region,用范围更大的扫描成本换区省去G1记忆集的维护成本。因此,ZGC的重分配只是决定了里面的存活对象会被重新复制到其他Region中,里面的Region会被释放,而并不能说回收行为就只是针对这个集合里面的Region进行的。因为标记过程是针对全堆的。

  3. 并发重分配
    重分配是ZGC执行过程中的核心阶段,这个过程要把重分配集中的存活对象复制到新的Region中,并为重分配集中的每个Region维护一个转发表,记录从旧对象到新对象的转向关系。

  4. 并发重映射
    重映射所做的就是修正整个堆中指向重分配集中就对象的所有引用,这一点从目标角度看与Shenandoah并发引用更新阶段一样,但是ZGC的并发重映射并不是一个必须要 玻切去完成的任务,因为前面说的。

内存分配和回收策略
  • 大对象有限在Eden分配
  • 大对象直接进入老年代
  • 长期存活的对象将进入老年代
  • 动态对象年龄判定
  • 空间分配担保

虚拟机监控、故障处理工具

jps 虚拟机进程状况工具

jps(JVM Process Status Tool),它的功能和ps命令类型,可以列出正在运行的虚拟机进程,

jstat 虚拟机统计信息监视工具

jstat(JVM Statistics Monitoring Tool)用于监视虚拟机各种运行状态信息的命令行工具,它可以显示本地或者远程虚拟机进程中的类加载器、内存、即时编译等运行信息。

jinfo java配置信息工具

jinfo可以实时查看和调整虚拟机各项参数,使用jps命令的-v参数可以查看看虚拟机启动时显示指定的参数列表,但如果想知道未被侠士指定的参数的系统默认值,除了去找资料外,就只能使用jinfo -flga进行查询了。

jmap java内存映射工具

jmap命令用于生成对转储快照(heapdump或dump文件),如果不想用jmap命令,可以添加参数-XX:+HeapDumpOnOutOfMemoryError,或者在Linux系统下,通发送进程退出信号 恐吓 一下虚拟机,也能顺利拿到堆转储快照。

jhat 虚拟机对转储快照分析工具

JDK提供了jhat命令与jmap搭配使用,来分析jmap生成的堆转储快照,jhat内置了一个微型的HTTP/Web服务器,生成转储快照的分析结果后,可以再浏览器中查看,这个jhat很少用,可以使用VisualVM以及专业的分析工具。

jstack java堆栈跟踪工具

jstack 命令用于生成虚拟机当前时刻的线程快照一般称为threaddump或者javacore文件。线程快照就是当前虚拟机内没一条线程正在执行的方法,线程快照及时当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,线程键死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因。

其他基础工具

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入理解JVM第四版》是一本关于Java虚拟机(JVM)原理和实现的经典著作。它由周志明所著,共计300页。这本书的主要目的是教会读者如何深入理解并掌握JVM的工作原理和内部机制。 本书首先介绍了JVM的基本概念和结构。它详细解释了JVM如何加载、验证、解析和初始化Java类。此外,书中还涉及了运行时数据区域的结构和功能,包括堆、栈、方法区等。 接下来,本书讨论了JVM的垃圾回收机制。它介绍了不同类型的垃圾回收算法和相关的性能调优技术。读者可以通过阅读这一部分,了解如何优化程序的内存使用和垃圾回收效率。 此外,本书还涵盖了JVM的即时编译器和优化技术。它详细介绍了JIT编译器的工作原理,并解释了常用的优化技术,如内联、逃逸分析和锁消除等。这对于那些希望通过编写高效的Java代码来提高程序性能的开发人员来说非常有用。 最后,本书还提供了一些高级主题,如类加载器、字节码增强和调试技术。通过阅读这些章节,读者可以加深对JVM内部机制的理解,并学习如何调优和调试JVM相关的问题。 总体而言,《深入理解JVM第四版》是一本全面而深入的JVM学习资料。它适合那些希望更深入了解JVM内部工作原理的Java开发人员。无论是学生、工程师还是研究人员,都可以从这本书中获得宝贵的知识和技巧。读者可以通过仔细阅读和实践书中的示例代码,提升自己的Java编程能力和理解JVM的水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值