JAVA虚拟机学习笔记

JAVA虚拟机学习笔记

1.java虚拟机运行时数据区模型

这里写图片描述

  • 程序计数器:是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。此内存区域是唯一一个java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
  • java虚拟机栈:描述的是java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)。
  • 本地方法栈:与java虚拟机栈所发挥的作用非常相似,他们之间的区别不过是java虚拟机栈为虚拟机执行的是java代码(也就是字节码)的服务,本地方法栈则为虚拟机使用到的Native方法服务。
  • java堆:java虚拟机所管理的内存中最大的一块。java堆是被所有线程所共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例。java堆是垃圾收集器主要管理区域,所以也成为GC堆。
  • 方法区:与java堆一样,是各个线程所共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它有一个别名Non-Heap(非堆)。
  • 运行时常量池:是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量(即常量)和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。(常量池中不直接存储字符串常量,存储的是首次创建此字符串的引用)
  • 直接内存:并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。在JDK1.4中新加入I/O方式,它可以使用Native函数库直接分配对外内存,然后通过一个存储在java堆中的DircetByteBuffer对象作为这块内存的引用进行操作。

2.对象的创建过程

3.对象的内存布局

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3个区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)

  • 对象头:包括俩部分,第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,另一部分是类型指针,即对象指向它的类元数据的指针。
  • 实例数据:是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录下来。
  • 对齐填充:并不是必然存在的,也没有特别的含义,他仅仅起着占位符的作用。

4.垃圾收集器与内存分配策略

4.1 判断对象是否存活

4.1.1 引用计数算法

给对象中添加一个引用计算器,每当有一个地方引用它,计数器的值就加 1;当引用失效就减 1;任何时刻计算器为 0 的对象就是不可能再被使用的。

4.1.2 可达性分析算法

这个算法的基本思路就是通过一系列的称为“ GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots没有任何引用链相连(即从 GC Roots到这个对象不可达)时,则证明此对象时不可用的。

在 java 语言中 ,可作为 GC Roots 的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中 JNI (即一般说的是 Native 方法)引用的对象
4.1.3 生存还是死亡

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候他们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少经历俩次标记过程:如果对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那他将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize() 方法。当对象没有覆盖 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过,虚拟机将这俩种情况都视为“没有必要执行”。

如果这个对象被判定为有必要执行 finalize() 方法,那么这个对象将会放置一个叫做 F-Queue 的队列之中,并在稍后由一个虚拟机自动建立的、低优先级的 Finalizer 线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但是并不承诺等待它运行结束,这个做的原因是,如果一个对象在 finalize() 方法中执行缓慢,或者发生了死循环(更极端的情况),将可能会导致 F-Queue 队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。 finalize() 方法是对象逃脱死亡命运的最后一次机会,稍后 GC 将对 F-Queue 中的对象进行第二次小规模的标记,如果对象要在 finalize() 中成功拯救自己—— 只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this 关键字)赋值给某个类变量或者对象的成员变量,拿在第二次标记时它将被移除“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

4.2 垃圾收集算法

4.2.1 标记 - 清楚算法

算法分为“标记”和“清楚”俩阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

它的主要不足有俩个:一个是效率问题,标记和清楚俩个过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后再程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

4.2.2 复制算法(主要用于新生代)

为了解决效率问题,一种称为“复制”的收集算法出现了,它将可用内存按容量划分大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后在把已使用过的内存空间一次清理掉。这样使得每次都对整个半区进行内存回收,内存分配时也不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算的代价将内存缩小为了原来的一半,未免太高了一点。

IBM研究新生代中的对象98%是“朝生暮死”的,所以不需要按照 1:1 的比例来划分内存空间,而将内存分为一块较大的Eden空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor 。当回收时,将 Eden 和 Survivor 中还存活这的对象一次性复制到另一款 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。当 Survivor 空间不够用是需要依赖其他内存(这里只老年代)进行分配担保。

4.2.3 标记 - 整理算法(主要用于老年代)

标记 - 整理算法,标记过程仍与“标记 - 清除”算法一样,但是后续步骤不是直接对可回收对象进行整理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

4.2.4 分代收集算法

根据对象存活周期的不同将内存划分几块。一般是把 JAVA 堆划分为新声代和老年代,根据各个年代的特点采用最适合的收集算法。在新生代中,每次垃圾收集都会发现有大批对象死去,只有少量存活,那就选用复制算法,只需付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外的空间对它进行分配担保,必须使用“标记 - 清理”或者“标记 - 整理”算法来进行回收。

4.3 垃圾收集器

4.3.1 Serial 收集器
4.3.2 ParNew 收集器
4.3.3 Parallel Scavenge 收集器
4.3.4 Serial Old 收集器
4.3.5 Parallel Old 收集器
4.3.6 CMS 收集器
4.3.7 G1 收集器

4.4 内存分配与回收策略

对象的内存分配,往大方向讲,就是在堆上分配(但也可能经过 JIT 编译后被拆散为标量类型并间接地栈上分配),对象主要分配在新生代的 Eden 区上,如果启动了本地线程分配缓冲,将按照线程优先在 TLAB 上分配。少数情况下也可能会直接分配在老年代中,分配的规则不是百分之百固定的,其细节取决于当前使用的哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。

  • 对象会优先在 Eden 分配:大多数情况下,对象在新生代 Eden 区中分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。
  • 大对象直接进入老年代:所谓的大对象是指,需要大量的连续内存空间的 java 对象,最典型的大对象就是那种很长的字符串以及数组。经常出现大对象容易导致内存还有不少空间是就提前触发垃圾收集以获取足够的连续空间。
  • 长期存活的对象将进入老年代:虚拟机给每个对象定义一个对象年龄(Age)计算器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并且对象年龄设为 1 。对象在 Survivor 区中每“熬过”一次 Minor GC ,年龄就增加一岁,当它的年龄增加到一定程度(默认为15),就将进入老年代。
  • 动态对象年龄判定:为了更好适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于等于的改年龄的对象直接进入老年代。
  • 空间分配担保:在发生 Minor GC 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么 Minor GC 可以确保是安全的。如果不成立,则虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次 Minor GC ,尽管这次 Minor GC 是有风险的;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那这是也要进行一次 Full GC.

5.虚拟机性能监控与故障处理工具

5.1-JDK的命令行工具

Sun JDK 监控和故障处理工具

名称主要作用
jpsJVM Process Status Tools,显示指定系统内所有的HostSpot虚拟机进程
jstatJVM Statistics Monitoring Tool,用于收集HostSpot虚拟机各方面的运行数据
jinfoConfiguration Info for Java,显示虚拟机配置信息
jmapMemory Map for Java,生成虚拟机的内存转储快照(heapdump文件)
jhatJVM Heap Dump Browser,用于分析heapdump文件,他会建立一HTTP/HTML服务器,让用户可以在浏览器查看分析结果
jstackStack Trace for Java,显示虚拟机的线程快照
5.1.1-jps 虚拟机进程状况工具

jps命令格式:

jps [ options ] [ hostid ]

jps工具主要选项

选项作用
-q只输出LVMID,省略主类的名称
-m输出虚拟机进程启动时传递给主类main()函数参数
-l输出主类的全名,如果进程执行的是 jar 包,则输出jar的路径
-v输出虚拟机进程启动时JVM参数
5.1.2-jstat 虚拟机统计信息监视工具

jstat命令格式为:

jstat [ option  vmid [ s | ms ] [ count ] ]

注意:option代表用户希望查询的虚拟机信息,主要分为3类:类加载、垃圾收集、运行期编译状况。

选项作用
-class监视类装载、卸载数量、总空间以及类装载所耗费的时间
-gc监视 java 对状况,包括 Eden 区、两个 Survivor 区、老年代、永久代等的容量、已用空间、GC 时间合计等信息
-gccapacity监视内容与 -gc 基本相同,但输出主要关注 java 堆各个区域使用到的最大、最小空间
  • gcutil
监视内容与 -gc 基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause与 -gcutil 功能一样,但是会额外输出导致上一次 GC 产生原因
-gcnew监视新生代 GC 状况
-gcnewcapacity监视内容与 -gnew 基本相同,输出主要关注使用到的最大、最小空间
-gcold监视老年代 GC 状况
-gcoldcapacity监视内容与 -gold 基本相同,输出主要关注使用到的最大、最小空间
-gcpermcapacity输出永久代使用到的最大、最小空间
-compiler输出 JIT 编译期编辑过的方法、耗时等信息
-printcompilation输出已经被 JIT 编译的方法
5.1.3-jinfo Java配置信息工具

jinfo命令格式:

jinfo [ option ] pid
5.1.4-jmap Java内存影像工具

jmap命令格式:

jmap [ option ] vmid

jmap工具主要选项

选项作用
-dump生成 java 堆转储快照。格式为:-dump:[live, ] format=b, file=< filename >,其中 live 子参数说明是否只 dump 出存活的对象
-finalizerinfo显示在 F-Queue 中等待 Finalizer 线程执行 Finalize() 方法的对象。只有在 Linux/Solaris 平台有效
-heap显示 JAVA堆详细信息,如使用哪种回收器、参数配置、分代状况等。只有在 Linux/Solaris 平台有效
-histo显示堆中对象统计信息,包括类、实例数量、合计容量
-permstat以 ClassLoader 为统计口径显示永久代内存状态。只有在 Linux/Solaris 平台有效
-F当虚拟机进程对 -dump 选项没有响应时,可使用这个选项强制生成 dump 快照。只有在 Linux/Solaris 平台有效
5.1.5-jhat 虚拟机堆转储快照分析工具
5.1.6-jstack Java堆栈工具

jstack命令格式:

jstack [ option ] vmid

jstack工具主要选项

选项作用
-F当正常输出的请求不被响应时,强制输出线程堆栈
-l除堆栈外,显示关于锁的附加信息
-m如果调用到本地方法的话,可以显示 C/C++的堆栈
5.1.7-HSDIS: JIT生成代码反汇编

5.2-JDK的可视化工具

俩个功能强大的可视化工具:JConsole和VisualVM

5.2.1-JConsole:Java监视与管理控制台
5.2.2-VisualVM:多合一故障处理工具

6.类文件结构

7.虚拟机类加载机制

7.1类加载的时机

类从被加载到虚拟机内存中开始,到卸载出内初为止,他的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析部分统称为连接(Linking)。

这里写图片描述

加载、验证、准备、初始化和卸载这5个阶段是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段不一定:它在某些情况下可以在初始化阶段之后在开始。

初始化阶段,虚拟机规范严格规定了有且只有5种情况必须立即对类进行“初始化”(而加载、验证、准备自然需要在此之前开始)

  1. 遇到 new、getstatic、putstatic 或 invokestatic 这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
  2. 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。
  3. 当初始化一个类的时候,如果发现其父类没有进行过初始化,则需要先触发其父类的初始化。
  4. 当虚拟机启动时,用户需要制定一个要执行的主类(包含 main()方法的那个类),虚拟机会先初始化这个主类。
  5. 当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

7.2 类加载器

7.2.1 类与类加载器
7.2.2 双亲委派模型
7.2.3 破坏双亲委派模型

严重申明

本文完全参考周志明老师的《深入理解 java 虚拟机–JVM 高级特性与最佳实践》一书,特别感谢周志明老师的辛苦付出。本文完全用于博主的自我学习和与 java 学习爱好者的分享,若用于商业用途请自觉联系周志明老师。谢谢。

通过短时倒谱(Cepstrogram)计算进行时-倒频分析研究(Matlab代码实现)内容概要:本文主要介绍了一项关于短时倒谱(Cepstrogram)计算在时-倒频分析中的研究,并提供了相应的Matlab代码实现。通过短时倒谱分析方法,能够有效提取信号在时间与倒频率域的特征,适用于语音、机械振动、生物医学等领域的信号处理与故障诊断。文中阐述了倒谱分析的基本原理、短时倒谱的计算流程及其在实际工程中的应用价值,展示了如何利用Matlab进行时-倒频图的可视化与分析,帮助研究人员深入理解非平稳信号的周期性成分与谐波结构。; 适合人群:具备一定信号处理基础,熟悉Matlab编程,从事电子信息、机械工程、生物医学或通信等相关领域科研工作的研究生、工程师及科研人员。; 使用场景及目标:①掌握倒谱分析与短时倒谱的基本理论及其与傅里叶变换的关系;②学习如何用Matlab实现Cepstrogram并应用于实际信号的周期性特征提取与故障诊断;③为语音识别、机械设备状态监测、振动信号分析等研究提供技术支持与方法参考; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,先理解倒谱的基本概念再逐步实现短时倒谱分析,注意参数设置如窗长、重叠率等对结果的影响,同时可将该方法与其他时频分析方法(如STFT、小波变换)进行对比,以提升对信号特征的理解能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值