JVM 核心技术 - 知识点整理

news2024/11/16 23:58:08

JVM 核心技术

JAVA 前言

  • JDK (Java Development Kit) = JRE + 开发工具
  • JRE(Java Runtime Environment) = JVM (Java Virtual Machine) + 类库
  • 一次编译,到处执行的原理: java字节码可以在有JVM的环境中运行,不关心操作系统的版本,减少了调试不同环境兼容性的工作。

系统指标

  • 业务需求指标:如吞吐量(QPS、TPS)、响应时间(RT)、并发数、业务成功率等。
  • 资源约束指标:如 CPU、内存、I/O 等资源的消耗情况。

字节码

如何读懂字节码

    javap -c HelloWorld.class -- 反编译
    javap -c verbose HelloWorld.class -- 输出附加信息

    案例一: 
    #1 = Methodref #4.#13 // java/lang/Object."<init>":()V
    
    含义: 
        #1 常量编号, 该文件中其他地方可以引用。
        = 等号就是分隔符.
        Methodref 表明这个常量指向的是一个方法;具体是哪个类的哪个方法呢? 类指向的 #4, 方法签名指向的 
        #13; 当然双斜线注释后面已经解析出来可读性比较好的说明了。
    
    案例二: 
    public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1

    含义:
        ([Ljava/lang/String;)V : 其中小括号内是入参信息/形参信息;左方括号表述数组;L 表示对象;后面的java/lang/String就是类名称;小括号后面的 V 则表示这个方法的返回值是 void;
        flags: ACC_PUBLIC, ACC_STATIC : 表示 public 和 static
        stack=2, locals=2, args_size=1 : 执行该方法时需要的栈(stack)深度是多少,需要在局部变量表中保留多少个槽位, 还有方法的参数个数

    注意: 无参构造函数的参数个数是1, 因为this也要占一个位置。

    案例三:
    初始化对象三部曲:
    初始化对象
    - new 占三位,创建对象
    - dup 复制栈顶引用值
    - invokespecial 执行对象初始化

    astore {N} or astore_{N} – 赋值给局部变量,其中 {N} 是局部变量表中的位置。
    iconst_{N} 用来将常量值N加载到栈里面
    istore_{N} 将变量存储到在 LocalVariableTable 的槽位 N 中。
    dstore 4 将 double 值保存到本地变量4号槽位
    putfield – 将值赋给实例字段
    putstatic – 将值赋给静态字段
    swap 指令用来交换栈顶两个元素的值
    pop 指令则从栈中删除最顶部的值。
    dup 指令复制栈顶元素的值。
    dup_x1 将复制栈顶元素的值,并在栈顶插入两次
    dup2_x1 则复制栈顶两个元素的值,并插入第三个值

类加载器

  • 一个类在 JVM 里的生命周期有 7个阶段,分别是加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(
    Initialization)、使用(Using)、卸载(
    Unloading)。

  • JVM自带的类加载器 (使用双亲委托机制加载类)

    • 启动类加载器(BootstrapClassLoader)- 加载 Java 的核心类,代码打印为null
    • 扩展类加载器(ExtClassLoader)- 加载 JRE 的扩展目录,代码打印为null
    • 应用类加载器(AppClassLoader)- 加载系统属性指定的 jar 包和类路径
  • 自定义类加载器

    • 继承ClassLoader 类,覆写方法来实现自定义加载器。

Java 内存模型

  • JVM 内存结构可分为 线程栈 + 堆内存 + 堆外内存。
  • JVM 堆由 heap + non-heap组成, heap由 yong[Eden, Survivor] + old 组成, non-heap 由 Metaspace + CCS(存放 class 信息的) +
    Code Cache (
    存放 JIT 编译器编译后的本地机器代码) 组成
  • 由于线程的工作时会去主内存拷贝一份成员变量副本回工作空间,所以就有了内存可见性问题。解决如下
    • synchronized 会让变量回主内存取值
    • volatile 保证内存可见性,使用读写屏障禁止cpu指令重排序,更新缓存时,标记主存的值已经失效,通知其他副本更新。

注意: 有些人测试时发现没有生效,可能由于使用了println(),或者多个线程执行很快,thread1改完,thread2才读。

JVM 启动参数

-Duser.timezone=GMT+08  // 设置用户的时区为东八区
-Dfile.encoding=UTF-8      // 设置默认的文件编码为UTF-8
java -XshowSettings:properties -version # 查看默认的所有系统属性
java -XshowSettings:vm -version # 查看 VM 设置
java -XshowSettings:locale -version # 查看当前 JDK/JRE 的默认显示语言设置
mvn package -Djava.test.skip=true / mvn package -DskipTests # 不执行单元测试
-Xmx4g, 指定最大堆内存为4g,一般为70%~80%的系统可用内存
-Xms4g, 指定堆内存空间的初始大小为4g
-Xss1m, 设置每个线程栈的字节数为1MB,与-XX:ThreadStackSize=1m等价
-verbose:gc 在 GC 日志中输出详细的GC信息,可动态开关
-XX:+PrintGCDetails 和 -XX:+PrintGCTimeStamps:打印 GC 细节与发生时间, 使用Xlog:gc* 代替
-Xloggc:file:与-verbose:gc功能类似,只是将每次 GC 事件的相关情况记录到一个文件中
-XX:+UseG1GC:使用 G1 垃圾回收器
-XX:+UseConcMarkSweepGC:使用 CMS 垃圾回收器
-XX:+UseSerialGC:使用串行垃圾回收器
-XX:+UseParallelGC:使用并行垃圾回收器
-XX:+HeapDumpOnOutOfMemoryError 产生oom自动dump堆内存
-XX:HeapDumpPath 指定dump文件目录
-XX:OnError="shell" 发现致命异常时,执行一个shell
-XX:OnOutOfMemoryError 抛出 OutOfMemoryError 错误时执行的脚本
-XX:ErrorFile=filename 致命错误的日志文件名,绝对路径或者相对路径。
javac GCTest.java # 编译
java -Xss200k -verbose:gc -Xmx5m -Xms1m -Xlog:gc* -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath="C:\work\java-base\src" -XX:+TraceJNIHandleAllocation GCTest # 执行命令 且加入参数

可以使用 https://www.eclipse.org/mat/ 内存分析工具来看内存泄漏问题

Java Debug 工具

tool nameinfo
javacJDK 内置的编译工具
javap反编译 class 文件的工具
javadoc根据 Java 代码和标准注释,自动生成相关的 API 说明文档
jps展示 Java 进程信息(列表)
jstat用来监控 JVM 内置的各种统计信息,主要是内存和 GC 相关的信息。
jmap主要用来 Dump 堆内存
jcmd本地诊断工具,只支持连接本机上同一个用户空间下的 JVM 进程
jstack打印出 Java 线程的调用栈信息.一般用来查看存在哪些线程,诊断是否存在死锁等。
jinfo查看具体生效的配置信息以及系统属性,还支持动态增加一部分参数。
jrunscript 和 jjs执行脚本.都是 JDK 8 自带的 JavaScript 引擎 Nashorn
jconsole可以从多个维度和时间范围去监控一个 Java 进程的内外部指标。进而通过这些指标数据来分析判断 JVM 的状态,为我们的调优提供依据。
jvisualvm效果同上,但是可以加插件
JDWP全称是 Java Debug Wire Protocol,中文翻译为“Java 调试连接协议”,是用于规范调试器(Debugger)与目标 JVM 之间通信的协议。
jdb启用了 JDWP 之后,可以使用各种客户端来进行调试/远程调试。

特殊手法: 定义MBean, 通过修改MBean的成员变量来修改程序的行为

GC 算法

  • 引用计数:每个对象有自己的被引用的计数,为0时会被统一清理
  • 标记清除算法(Mark and Sweep)
    • 过程中需要暂停系统(stop the world)来做可达性分析
    • 由于Mark之后需要整理内存碎片,防止碎片过多导致内存不可分配
    • 为了让整理碎片时间更短,引出了分代概念,将heap分为了 young (eden[有多个 线程本地分配缓冲区 TLAB] + s0 + s1) + old
  • 标记整理
    • 由于标记清除后,内存会有很多段空白,可能导致一个有很多空间,但是放不下一个大对象。
      由此引入整理,在清除后,将所有的数据整理到一块,这样就空出了一个连续的空间可以分配对象
  • 标记复制
    • 标记对象后,将存活对象复制到新的空间,然后清除旧的。 优点是可以同时进行标记和复制,缺点是需要额外的空间。

垃圾收集器

  • 串行 GC(Serial GC)
    • 串行 GC 对年轻代使用 mark-copy(标记—复制)算法,对老年代使用 mark-sweep-compact(标记—清除—整理)算法。
    • 该选项只适合几百 MB 堆内存的 JVM,而且是单核 CPU 时比较有用。
  • 并行 GC(Parallel GC)
    • 在年轻代使用“标记—复制(mark-copy)算法”,在老年代使用“标记—清除—整理(mark-sweep-compact)算法”
    • 在执行“标记和复制/整理”阶段时都使用多个线程
    • 并行垃圾收集器适用于多核服务器,主要目标是增加吞吐量 (清理速度加快,多线程导致停顿久一点点)
  • CMS 垃圾收集器
    • 对年轻代采用并行 STW 方式的 mark-copy(标记—复制)算法,对老年代主要使用并发 mark-sweep(标记—清除)算法。
    • 不对老年代进行整理,使用空闲列表(free-lists)来管理内存空间的回收
    • mark-and-sweep(标记—清除)阶段没有STW,但是仍有CPU抢占。
    • 如果服务器是多核 CPU,并且主要调优目标是降低 GC 停顿导致的系统延迟,那么使用 CMS 是个很明智的选择。
    • 流程: Initial Mark(初始标记, 会STW) -> Concurrent Mark(并发标记, 同步骤一一起运行) -> Concurrent Preclean(并发预清理)-> Concurrent Abortable Preclean(可取消的并发预清理)-> Final Remark(最终标记, 第二次STW)-> Concurrent Sweep(并发清除)-> Concurrent Reset(并发重置, 为下次GC做数据初始化)
    • 老年代不会碎片整理,可能有问题
  • G1 垃圾收集器
    • 垃圾最多的小块会被优先收集。这也是 G1 名称的由来。
    • 尽可能满足配置的停顿时间处理垃圾回收, -XX:+UseG1GC -XX:MaxGCPauseMillis=50
    • 堆不再分成年轻代和老年代,而是划分为多个(通常是 2048 个)可以存放对象的小块堆区域, 这些小块可能属于eden,survivor,old
    • G1 适合大内存,需要较低延迟的场景。
    • 步骤: Initial Mark(初始标记)-> Root Region Scan(Root 区扫描)-> Concurrent Mark(并发标记)-> Remark(再次标记, 会STW) -> Cleanup(清理)
    • 转移暂停:混合模式(Evacuation Pause(mixed)): 清理后会开始整合对象,标记整理一样。
    • Remembered Sets(历史记忆集)简介: 每个小块都有一些自身引用的记忆,用于并发标记
  • ZGC 垃圾收集器
    • 通过染色指针和读屏障来标记和清理垃圾,期间也有STW,但由于工作量很小,所以都很快,并且扫描都是并发的,只有在mark的一些阶段会STW, 延迟在10ms以内。
    • 步骤: 暂停—标记开始阶段(STW)[标记根对象集合指向的对象] -> 并发标记/重映射阶段 [遍历对象图结构,标记对象] -> 暂停—标记结束阶段(STW)[同步点,弱根对象清理] -> 并发准备重定位阶段 [引用处理、弱对象清理等] -> 暂停—重定位开始阶段 (STW) [根对象指向重定向集合] -> 并发重定位阶段 [重定向集合中的对象重定向]
    • 染色指针标记对象:Marked0、Marked1、Remapped、Finalizable
  • Shenandoah GC
    • 早于ZGC,与ZGC定位类似。
    • 步骤:初始标记阶段 STW -> 并发标记阶段 -> 最终标记阶段 STW -> 并发清理阶段 -> 并发转移阶段 -> 初始引用更新阶段 STW -> 并发引用更新阶段 -> 最终引用更新阶段 STW -> 并发清理阶段
      GC 内存治理由大到小,阶段由简入繁。

延迟: 一次请求的往返延迟

吞吐量: 一般指相当一段时间内测量出来的系统单位时间处理的任务数或事务数

Oracle GraalVM: 是 Oracle 开源的一款通用虚拟机产品,官方称之为 Universal GraalVM,是新一代的通用多语言高性能虚拟机。能执行各类高性能与互操作性任务,在无需额外开销的前提下允许用户构建多语言应用程序。

  • 可以为多种语言提供编译服务,并且有较大的执行性能提升。

实践

  • GC 分析工具

    • GCEasy
    • FastThread
    • HeapHero
    • GCViwer -> 开源
    • BTrace -> 问题追踪
    • Arthas -> 问题追踪 + 性能分析
  • Thread 分析

    • 创建过程: Thread.start() -> JVM Thread 对象创建 -> OS 系统的 Thread 创建 -> JVM 线程栈内存分配 -> 启动 Thread.run() -> 销毁JVM对象同时销毁OS Thread
    • 所以的Thread对象在JVM中都由一个Threads_list管理
    • JVM线程状态:_thread_new[初始化的新线程] -> _thread_in_Java[正在执行 Java 代码的线程] -> _thread_in_vm[JVM 内部执行的线程] -> _thread_blocked[由于某种原因被阻塞的线程,获取锁、等待条件、休眠、执行阻塞的 I/O 操作]
    • threadA.join() 让执行代码的线程等ThreadA执行完毕再继续
    • Java Synchronization 是由操作系统提供的管程(Monitor)实现的。管程有锁定、解锁两种状态,可以用来实现线程的同步访问。 在java 对象头上会存储对象的锁标志(2bit,00,01,10,11)来确定锁的类型,无锁 -> 偏向-> 轻量-> 重量
    • 自旋锁:在争取锁时,失败后先自旋一会,没准就获取到了,但是也可能浪费cpu。 适应性自旋:根据上次自旋次数来决定当前自旋次数。
    • http://fastthread.io/ 使用FastThread 来分析dump下来的Thread日志,有图形界面,更加直观。
    • GC的工作线程一般是总CPU*5/8,实际核心数 < 系统检测到的, 会导致频繁的切换上下文而性能降低
  • JVM 内存分析

    • 对象头占用8字节(64位在没有开启指针压缩时也是8,否则时12字节,但是实际时16字节,8的倍数)
    • JVM遵循对齐,按照8的倍数分配内存
    • int 4字节,char 1字节,对象头占用8字节。 在申请空间时由大到小排序,先分配大的,在分配小的
    • MAT 全称是 Eclipse Memory Analyzer Tools。 用于分析内存泄露
    • errors 分析:
      • OutOfMemoryError: Java heap space -> 加大内存,代码调优
      • OutOfMemoryError: GC overhead limit exceeded -> GC无法回收垃圾,内存也满了。最好进行代码优化
      • OutOfMemoryError: PermGen space -> 永久代空间满了,加大内存或者JVM参数调优
      • OutOfMemoryError: Metaspace -> 元空间满了,方法如上
      • OutOfMemoryError: Unable to create new native thread -> 无法创建更多线程,MAT内存分析,优化代码
      • OutOfMemoryError: Out of swap space -> 系统物理内存分配不足,系统调优+代码优化
      • OutOfMemoryError: Requested array size exceeds VM limit -> 分配空间超过JVM允许范围,代码检查是否存在问题
      • OutOfMemoryError: Kill process or sacrifice child -> 内存超分配导致内存不足然后按评分(高)杀死进程,OOM killer 调优 / 加大内存
    • 影响GC的因素
      • 高分配速率(High Allocation Rate)-> eden区单位时间内创建对象很多会导致频繁的回收对象。
      • 过早提升(Premature Promotion) -> 大量对象由于频繁Young-GC而升迁到老年代,从而导致老年代频繁GC
      • 对象检查越多,暂停时间越长,导致GC越慢
    • 引用类型对GC的影响
      • Weak(弱):如果只有弱引用对象关联到当前对象,GC可以强制回收。
      • Soft(软):由GC自己决定,一般内存不足时才会回收
      • Phantom(虚):只有手动调用clear方法才会被回收

内存溢出: 正常原因导致内存不足

内存泄露: 没用的对象太多并且没有被回收导致内存不足

xx

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22803.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Cloud版本,Spring Boot版本详细对应关系

目录 一、官网&#xff08;网页版&#xff09; 二、官网&#xff08;API接口&#xff09; 三、根据历史官方文档梳理、保存的表格 四、官方&#xff08;wiki&#xff09;Spring Cloud的各个组件与Spring Boot支持、对应关系 有多个方式可以查看Spring Boot和Spring Cloud版本…

嵌入式分享合集107

一、Wi-Fi HaLow Wi-Fi HaLow很快就会出现在我们日常生活中的智慧门锁、安保摄影机、可穿戴设备和无线传感器网络上。什么是Wi-Fi HaLow&#xff1f;与传统的Wi-Fi&#xff08;4/5/6&#xff09;有何不同&#xff1f;究竟是什么让Wi-Fi HaLow成为物联网的理想协议&#xff1f;…

【初阶数据结构】——带头双向循环链表(C描述)

文章目录前言带头双向循环链表实现1. 结构介绍2. 结点创建3. 初始化4. 销毁5. 头插6. 头删7. 尾插8. 尾删9. 打印10. 查找11. 在pos之前插入数据12. 删除pos位置13. 判空14. 计算大小源码展示1. DoubleList.h2. DoubleList.c3. Test.c前言 上一篇文章我们学习了单链表&#xff…

SpringBoot自动装配原理

目录一、前言二、SpringBoot自动装配核心源码2.1、SpringBootApplication2.2、EnableAutoConfiguration2.3、Import(AutoConfigurationImportSelector.class)2.3.1、selectImports方法2.3.2、getAutoConfigurationEntry方法2.3.3、getCandidateConfigurations方法2.3.4、Spring…

在阿里云 ACK 上部署 EMQX MQTT 服务器集群

云进入以「应用为中心」的云原生阶段&#xff0c;Operator 模式的出现&#xff0c;则为 Kubernetes 中的自动化任务创建配置与管理提供了一套行之有效的标准规范。通过将运维知识固化成高级语言 Go/Java 代码&#xff0c;使得运维知识可以像普通软件一样交付&#xff0c;并能支…

欧姆龙NJ/NX基于Sysmac Studio的EIP通讯 方式

目录 Omorn - NJ301-1100 AND NX102-9000 EIP - Sysmac Studio 测试案例IP 创建变量类型 通讯配置 控制器程序下载 通讯测试 Omorn - NJ301-1100 AND NX102-9000 EIP - Sysmac Studio 测试案例IP 创建变量类型 通讯配置 控制器程序下载 通讯测试 Omorn - NJ301-1100…

Go语言快速入门笔记

文章目录import匿名导包和别名导包的方式defer语句数组和动态数组固定长度数组切片&#xff08;动态数组&#xff09;切片的容量追加和截取map面向对象struct继承多态interface空接口万能类型与类型断言机制变量的内置pair结构变量结构reflect包(反射)reflect反射解析结构体标签…

【Java毕设】基于idea Java的在线考试系统(附源码+课件)

项目介绍&#xff1a; 本系统是一个基于java的在线考试系统。它的用户由学生、教师和系统管理员组成。学生登陆系统可以进行在线测试和成绩查询。当学生登陆时&#xff0c;系统会随机地为学生选取试题组成考卷。当学生提交考卷后&#xff0c;系统会自动批改客观题&#xff0c;…

html实现爱情告白(附源码)

文章目录1.设计来源1.1 主界面1.2 执子之手&#xff0c;与子偕老1.3 死生契阔&#xff0c;与子成说1.4 生当复来归&#xff0c;死当长相思1.5 自君之出矣&#xff0c;明镜暗不治1.6 思君如流水&#xff0c;何有穷已时1.7 南有乔木&#xff0c;不可休思1.8 汉有游女&#xff0c;…

快递查询工具,一键查物流,派件时效怎么分析

快递发货后&#xff0c;该如何快速查询到物流信息、比如怎么分析派件时效呢&#xff1f;今天小编给大家分享一个新的技巧&#xff0c;它支持多家快递&#xff0c;一次能查询多个单号物流&#xff0c;还能对查询到的物流进行分析、导出以及筛选&#xff0c;下面一起来试试吧。 …

3000万人气的腾格尔,会和金鸡奖提名电影《巴林塔娜》合作吗

刚刚结束的2022年11月19日&#xff0c;对于“草原歌神”腾格尔来说&#xff0c;注定是要被载入史册的一天。2022年11月19日&#xff0c;是卡特尔世界杯开幕式的前一夜&#xff0c;腾格尔老师也通过某音平台&#xff0c;开启了自己的线上演唱会。 说起明星们的演唱会&#xff0c…

redis 登录案例

下图就是登录controller Controller public class LoginController {RequestMapping("/login")public String Login(String username, String password, HttpServletResponse response){System.out.println(username);System.out.println(password);//判断账号密码 …

微信小程序 | IM交友聊天功能大汇总

&#x1f4cc;个人主页&#xff1a;个人主页 ​&#x1f9c0; 推荐专栏&#xff1a;小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏&#xff01;从个人到商业的全套开发教程&#xff0c;实打实的干货分享&#xff0c;确定不来看看&#xff1f; …

关系数据库系统中的 NULL 值及其用途

在数据库中&#xff0c;NULL值具有非常特殊的含义。因此&#xff0c;重要的是要理解NULL值不同于零值或包含空格的字段。在今天的博客中&#xff0c;我们将探讨 NULL 值的含义以及如何在 Navicat Premium 中使用NULL。 什么是NULL&#xff1f; 应该注意的是&#xff0c;NULL值…

Linux上部署Kubectl(k8s)

Linux上部署Kubectl(k8s) 1.k8s简介 1.1 Kubernetes 概念 在 k8s 上进行部署前&#xff0c;首先需要了解一个基本概念 Deployment Deployment 译名为 部署。在k8s中&#xff0c;通过发布 Deployment&#xff0c;可以创建应用程序 (docker image) 的实例 (docker container)…

跑步需要哪些运动装备?跑步爱好者者的装备推荐

一开始我认为跑步是不需要装备的&#xff0c;毕竟是基础运动&#xff0c;但问了一下身边的运动大神才明白在长期的跑步锻炼&#xff0c;特别是长跑的过程中好的装备不但可以保护你免受伤害&#xff0c;还能帮助你更好的掌握运动状态&#xff0c;进行合理的锻炼下面我就给大家列…

[附源码]java毕业设计网上书店的设计

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

vite+vue-router4.x配置动态路由

踩过的坑&#xff1a; import直接导入组件; router.addRoute 并不能一次性给你导入&#xff08;即不是vue-router3.x以下的addRoutes&#xff09;&#xff1b; addRoute后页面空白&#xff1b; 直接上才艺&#xff01; 我的设计思路是登录后获取token&#xff0c;并存入cookie…

bizlog通用操作日志组件(代码分析篇)

引言 在上篇博客中介绍了通用操作日志组件的使用方法&#xff0c;本篇博客将从源码出发&#xff0c;学习一下该组件是如何实现的。 代码结构 该组件主要是通过AOP拦截器实现的&#xff0c;整体上可分为四个模块&#xff1a;AOP模块、日志解析模块、日志保存模块、Starter模块…

企业小程序商城的推广方式有哪些_分享小程序商城的作用

其实搭建小程序商城比较容易&#xff0c;难的是后期的运营。要想办法进行引流&#xff0c;用户运营伙伴就给大家介绍一些引流推广的方法。 1、利用微信好友、微信群和朋友圈 可以让用户分享小程序给微信好友或微信群&#xff0c;这是吸引新用户的最快方法。除此之外&#xff0…