写点什么

你必须知道的一些 JVM 技术点

作者:Java学术趴
  • 2022 年 7 月 29 日
  • 本文字数:3778 字

    阅读完需:约 12 分钟

你必须知道的一些JVM技术点

👨‍🎓作者:Java 学术趴

🏦仓库:GithubGitee

✏️博客:CSDN掘金InfoQ云+社区

💌公众号:Java 学术趴

🚫特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系小编授权。

🙏版权声明:文章里的部分文字或者图片来自于互联网以及百度百科,如有侵权请尽快联系小编。微信搜索公众号 Java 学术趴联系小编。

☠️每日毒鸡汤:这个社会是存在不公平的,不要抱怨,因为没有用!人总是在反省中进步的!

👋大家好!我是你们的老朋友 Java 学术趴。今天给大家分享一下 Java 中的核心技术 JVM。作为一个 Java 程序员,相比或多或少的都会接触到一些关于 Java 底层的知识,这些底层知识是非常重要的,相比之下这些知识也是比较难以理解的,小编今天用大白话的方式给大家整理了一下必须要了解的技术点,可以便于大家的理解,喜欢的家人们点赞支持呦.....

23. 什么是指针碰撞?

一般情况下,JVM 的对象都放在堆内存中(发生逃逸分析除外)。当类加载检查通过后,Java 虚拟机开始为新生对象分配内存。如果 Java 堆中内存是绝对规整的,所有被使用过的的内存都被放到一边,空闲的内存放到另外一边,中间放着一个指针作为分界点的指示器,所分配内存仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的实例,这种分配方式就是 指针碰撞。

24. 什么是空闲列表?

如果 Java 堆内存中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,不可以进行指针碰撞啦,虚拟机必须维护一个列表,记录哪些内存是可用的,在分配的时候从列表找到一块大的空间分配给对象实例,并更新列表上的记录,这种分配方式就是空闲列表。

25. 什么是 TLAB?

可以把内存分配的动作按照线程划分在不同的空间之中进行,每个线程在 Java 堆中预先分配一小块内存,这就是 TLAB(Thread Local Allocation Buffffer,本地线程分配缓存) 。虚拟机通过 -XX:UseTLAB 设定它的。

26. 对象头具体都包含哪些内容?

在我们常用的 Hotspot 虚拟机中,对象在内存中布局实际包含 3 个部分:

  1. 对象头

  2. 实例数据

  3. 对齐填充

而对象头包含两部分内容,Mark Word 中的内容会随着锁标志位而发生变化,所以只说存储结构就好了。

  1. 对象自身运行时所需的数据,也被称为 Mark Word,也就是用于轻量级锁和偏向锁的关键点。具体的内容包含对象的 hashcode、分代年龄、轻量级锁指针、重量级锁指针、GC 标记、偏向锁线程 ID、偏向锁时间戳。

  2. 存储类型指针,也就是指向类的元数据的指针,通过这个指针才能确定对象是属于哪个类的实例。

27. 你知道哪些 JVM 调优参数?

堆栈内存相关

  • -Xms 设置初始堆的大小

  • -Xmx 设置最大堆的大小

  • -Xmn 设置年轻代大小,相当于同时配置-XX:NewSize 和-XX:MaxNewSize 为一样的值

  • -Xss 每个线程的堆栈大小

  • -XX:NewSize 设置年轻代大小(for 1.3/1.4)

  • -XX:MaxNewSize 年轻代最大值(for 1.3/1.4)

  • -XX:NewRatio 年轻代与年老代的比值(除去持久代)

  • -XX:SurvivorRatio Eden 区与 Survivor 区的的比值

  • -XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。-XX:MaxTenuringThreshold 设定对象在 Survivor 复制的最大年龄阈值,超过阈值转移到老年代

垃圾收集相关

XX:+UseParallelGC:选择垃圾收集器为并行收集器。

  • -XX:ParallelGCThreads=20:配置并行收集器的线程数

  • -XX:+UseConcMarkSweepGC:设置年老代为并发收集。

  • -XX:CMSFullGCsBeforeCompaction=5 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行 5 次 GC 以后对内存空间进行压缩、整理。

  • -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

辅助信息相关

  • -XX:+PrintGCDetails 打印 GC 详细信息

  • -XX:+HeapDumpOnOutOfMemoryError 让 JVM 在发生内存溢出的时候自动生成内存快照,排查问题用

  • -XX:+DisableExplicitGC 禁止系统 System.gc(),防止手动误触发 FGC 造成问题.

  • -XX:+PrintTLAB 查看 TLAB 空间的使用情况

28. 如何排查线上系统 CPU 飙高

在工作中,当一个系统发生 OOM 的时候,这种问题可能会让大家很烦恼困惑,因为故障排查起来是一个综合技术的考量,在平时工作中要增加自己的知识广度,多学习,多总结,多思考,多做笔记,这才是真正的王道。

尤其是在线上(Linux 环境)中,如何分析是哪个线程导致的 CPU 飙高的问题,通常大致有几个差不多固定的步骤。这个问题也是面试频率非常之高的问题之一,很多人也是靠回答这个问题而加薪。

常用套路步骤:

  • 使用 top 命令

  • 使用 top -H pid

  • 使用 prinf %x tid

  • 使用 jsack pid >pid.log

  • 查阅 less pid.log

下面就来说一下这几个步骤。

top

我们可以使用 top 命令来查找对应使用 CPU 最多的进程,找到后,先记录下对应的 pid(后面要用到)。

top -H pid

再次使用 top 名,但是这次增加一个参数-H,可以查看上面找出来的 pid 进程中对应的线程 tid,记住这时候的线程 tid 得记住。

printf

但是此时的 tid 是十进制的,我们需要把这个 tid 转成 16 进制。然后使用 printf %x tid。

stack

使用 jstack 工具把线程信息输出到对应的日志文件中,后面使用这个日志文件内容进行分析。jstack pid >pid.log

less

上面已经生成日志文件了,这时候可以使用 less 命令来查找上面已经转换好的 16 进制的线程 tid。

29. 如果对象的引用被置为 null,垃圾收集器是否会立即释放对象占用的内存?

不会立即释放对象占用的内存。 如果对象的引用被置为 null,只是断开了当前线程栈帧中对该对象的引用关系,而 垃圾收集器是运行在后台的线程,只有当用户线程运行到安全点(safe point)或者安全区域才会扫描对象引用关系,扫描到对象没有被引用则会标记对象,这时候仍然不会立即释放该对象内存,因为有些对象是可恢复的(在 fifinalize 方法中恢复引用 )。只有确定了对象无法恢复引用的时候才会清除对象内存。

30. 说说内存泄漏和内存溢出

内存泄露就是申请的内存空间没有被正确释放,导致内存空间就这么一点点被蚕食,最后就算再多的内存也被用光光。

内存溢出就是申请的内存超过了可用内存,内存不够用了,只有 10MB 的内存了,但现在需要使用 20MB 的内存

两者关系:内存泄露 → 剩余内存不足 → 后续申请不到足够内存 →内存溢出。

31. 什么是 Stop The World ? 什么是 OopMap?什么是安全点?

进行垃圾回收的过程中,会涉及对象的移动。为了保证对象引用更新的正确性,必须暂停所有的用户线程,像这样的停顿,虚拟机设计者形象描述为「Stop The World」。也简称为 STW。

在 HotSpot 中,有个数据结构(映射表)称为「OopMap」。一旦类加载动作完成的时候,HotSpot 就会把对象内什么偏移量上是什么类型的数据计算出来,记录到 OopMap。在即时编译过程中,也会在「特定的位置」生成 OopMap,记录下栈上和寄存器里哪些位置是引用。

这些特定的位置主要在:

  1. 循环的末尾(非 counted 循环)

  2. 方法临返回前 / 调用方法的 call 指令后

  3. 可能抛异常的位置

这些位置就叫作「安全点(safepoint)。」 用户程序执行时并非在代码指令流的任意位置都能够在停顿下来开始垃圾收集,而是必须是执行到安全点才能够暂停。

32. 说一下 JVM 有哪些垃圾回收器?

如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。下图展示了 7 种作用于不同分代的收集器,其中用于回收新生代的收集器包括 Serial、PraNew、ParallelScavenge,回收老年代的收集器包括 Serial Old、Parallel Old、CMS,还有用于回收整个 Java 堆的 G1 收集器。不同收集器之间的连线表示它们可以搭配使用。

  • Serial 收集器(复制算法): 新生代单线程收集器,标记和清理都是单线程,优点是简单高效;

  • ParNew 收集器 (复制算法): 新生代收并行集器,实际上是 Serial 收集器的多线程版本,在多核 CPU 环境下有着比 Serial 更好的表现;

  • Parallel Scavenge 收集器 (复制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 用户线程时间/(用户线程时间+GC 线程时间),高吞吐量可以高效率的利用 CPU 时间,尽快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景;

  • Serial Old 收集器 (标记-整理算法): 老年代单线程收集器,Serial 收集器的老年代版本;

  • Parallel Old 收集器 (标记-整理算法): 老年代并行收集器,吞吐量优先,Parallel Scavenge 收集器的老年代版本;

  • CMS(Concurrent Mark Sweep)收集器(标记-清除算法): 老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短 GC 回收停顿时间。

  • G1(Garbage First)收集器 (标记-整理算法): Java 堆并行收集器,G1 收集器是 JDK1.7 提供的一个新收集器,G1 收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。此外,G1 收集器不同于之前的收集器的一个重要特点是:G1 回收的范围是整个 Java 堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。

  • ZGC (Z Garbage Collector)是一款由 Oracle 公司研发的,以低延迟为首要目标的一款垃圾收集器。它是基于动态 Region 内存布局,(暂时)不设年龄分代,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的收集器。在 JDK 11 新加入,还在实验阶段,主要特点是:回收 TB 级内存(最大 4T),停顿时间不超过 10ms。优点:低停顿,高吞吐量, ZGC 收集过程中额外耗费的内存小。缺点:浮动垃圾

目前使用的非常少,真正普及还是需要写时间的。

新生代收集器:Serial、 ParNew 、 Parallel Scavenge

老年代收集器: CMS 、Serial Old、Parallel Old

整堆收集器: G1 , ZGC (因为不涉年代不在图中)。

发布于: 刚刚阅读数: 3
用户头像

Java学术趴

关注

还未添加个人签名 2022.07.02 加入

还未添加个人简介

评论

发布
暂无评论
你必须知道的一些JVM技术点_7 月月更_Java学术趴_InfoQ写作社区