内容纲要

内存管理

JVM中的堆,一般分为三大部分:新生代、老年代、永久代

新生代

主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。

新生代分为Eden区、ServivorFrom区、ServivorTo三个区。

  • Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
  • ServivorTo:保留了一次MinorGC过程中的幸存者。
  • ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。

当JVM无法为新建对象分配内存空间的时候(Eden满了),Minor GC被触发。因此新生代空间占用率越高,Minor GC越频繁。

MinorGC的过程:采用复制算法。

  1. 首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区)
  2. 同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区)
  3. 然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。

虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,  当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。

对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold (阈值)来设置。

老年代

老年代的对象比较稳定,所以MajorGC不会频繁执行。

在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

MajorGC采用标记—清除算法:

  1. 首先扫描一次所有老年代,标记出存活的对象
  2. 然后回收没有标记的对象。

MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。

当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

永久代

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制。

复制算法

P78-79

将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当一块的内存用完了,就将还存活着的对象复制到另外一块上面。

大多采用此算法去回收新生代。

Eden较大,两块Survivor较小。每次分配内存只使用Eden和一块Survivor。发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上,然后直接清理掉Eden和已用过的那块Survivor。

当Survivor空间不足以容纳一次Minor GC之后存活的对象时,就需要依赖其他内存区域(大多数是老年代)进行分配担保。

新生代老年代永久代

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

image-20211213192441060

jps:虚拟机进程状况工具

类似unix的ps命令

可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID

参数:
-q:只输出LVMID,省略主类的名称
-m:输出虚拟机进程启动时传递给主类main()函数的参数
-l:输出主类的全名,如果进程执行的是jar包,则输出jar路径
-v:输出虚拟机进程启动时的JVM参数

image-20211213200513090

image-20211213200527634

image-20211213200212941

image-20211213200444855

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

用于监视虚拟机各种运行状态信息的命令行工具。可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据。

image-20211213201417950

image-20211213201434357

类加载统计

jstat -clss (16016)

image-20211213202334351

编译统计

jstat -compiler (16016)

image-20211213202436507

垃圾回收统计

jstat -gc 16016

image-20211213202518051

堆内存统计

jstat -gccapacity 16016

image-20211213202553681

  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0C:第一个幸存区大小
  • S1C:第二个幸存区的大小
  • EC:伊甸园区的大小
  • OGCMN:老年代最小容量
  • OGCMX:老年代最大容量
  • OGC:当前老年代大小
  • OC:当前老年代大小
  • MCMN:最小元数据容量
  • MCMX:最大元数据容量
  • MC:当前元数据空间大小
  • CCSMN:最小压缩类空间大小
  • CCSMX:最大压缩类空间大小
  • CCSC:当前压缩类空间大小
  • YGC:年轻代gc次数
  • FGC:老年代GC次数

新生代垃圾回收统计

jstat -gcnew (16016)

image-20211213202913762

  • S0C:第一个幸存区大小
  • S1C:第二个幸存区的大小
  • S0U:第一个幸存区的使用大小
  • S1U:第二个幸存区的使用大小
  • TT:对象在新生代存活的次数
  • MTT:对象在新生代存活的最大次数
  • DSS:期望的幸存区大小
  • EC:Eden区的大小
  • EU:Eden区的使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间

新生代内存统计

jstat -gcnewcapacity (16016)

image-20211213203032234

  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0CMX:最大幸存1区大小
  • S0C:当前幸存1区大小
  • S1CMX:最大幸存2区大小
  • S1C:当前幸存2区大小
  • ECMX:最大Eden区大小
  • EC:当前Eden区大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代回收次数

老年代垃圾回收统计

jstat -gcold (16016)

image-20211213203129457

  • MC:方法区大小
  • MU:方法区使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • OC:老年代大小
  • OU:老年代使用大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

老年代内存统计

jstat -gcoldcapacity (16016)

image-20211213203411383

  • OGCMN:老年代最小容量
  • OGCMX:老年代最大容量
  • OGC:当前老年代大小
  • OC:老年代大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

元数据空间统计

jstat -gcmetacapacity (16016)

image-20211213203548297

  • MCMN:最小元数据容量
  • MCMX:最大元数据容量
  • MC:当前元数据空间大小
  • CCSMN:最小压缩类空间大小
  • CCSMX:最大压缩类空间大小
  • CCSC:当前压缩类空间大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

总结垃圾回收统计

jstat -gcutil (16016)

image-20211213201555352

E:新生代Eden区

S0,S1:新生代Survivor区

O:老年代

M:元数据空间(旧版为P:永久代)

YGC:Minor GC(Young GC) 362次

YGCT:YGC耗时362s

FGC:Full GC,122次

GCT:总共GC时间

JVM编译方法统计

jstat -printcompilation (16016)

image-20211213203745893

  • Compiled:最近编译方法的数量
  • Size:最近编译方法的字节码数量
  • Type:最近编译方法的编译类型。
  • Method:方法名标识。

jstat命令使用

jinfo:Java配置信息工具

实时查看和调整虚拟机各项参数。

使用jps命令的-v参数可以查看虚拟机启动时显式指定的参数列表。

使用jinfo的-flag选项进行查询未被显式指定的参数的系统默认值(如果只限于JDK 6或以上版本的话,使用javaXX:+PrintFlagsFinal查看参数默认值也是一个很好的选择)。

jinfo还可以使用-sysprops选项把虚拟机进程的System.getProperties()的内容打印出来。

加入了在运行期修改部分参数值的能力(可以使用-flag[+|-]name或者-flag name=value在运行期修改一部分运行期可写的 虚拟机参数值)。

image-20211213204319085

jmap:Java内存映像工具

1.用于生成堆转储快照;

其他:

XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在内存溢出异常出现之后自动生成堆转储 快照文件,

通过-XX:+HeapDumpOnCtrlBreak参数则可以使用[Ctrl]+[Break]键让虚拟机生成堆转储快 照文件,

又或者在Linux系统下通过Kill-3命令发送进程退出信号“恐吓”一下虚拟机,也能顺利拿到堆转 储快照。

2.查询finalize执行队列、Java堆和方法区的 详细信息,如空间使用率、当前用的是哪种收集器等。

image-20211213204544335

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

JDK提供jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来分析jmap生成的堆转储快照。

jhat内置了一个微型的HTTP/Web服务器,生成堆转储快照的分析结果后,可以在浏览器中查看。

(专业用于分析堆转储快照文件的Eclipse Memory AnalyzerIBM HeapAnalyzer等工具,能实现比jhat更强大专业的分析功能。)

就stack:Java堆栈跟踪工具

用于生成虚拟机当前时刻的线程快照。(线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因。线程出现停顿时通过jstack来查看各个线程的调用堆栈, 就可以获知没有响应的线程到底在后台做些什么事情,或者等待着什么资源。)

image-20211213204824470

其他

image-20211213204850891

image-20211213204916994

image-20211213204934744

123

image-20211213205041288

image-20211213205148509

image-20211213205206884

【书第四章】

功能强大的Visual VM

image-20211213211656651

类文件

分析Class文件字节码的工具:javap

javap -verbose TestClass

Java程序方法体的代码经过Javac编译器处理之后,最终变为字节码指令存储在Code属性内。接口或者抽象类中的方法不存在于Code属性。

max_locals单位:槽(Slot),变量槽是虚拟机为局部变量分配内存所使用的最小单位

Java虚拟机采用面向操作数栈,大多数指令都不包含操作数,只有一个操作码,指令参数都存放在操作数栈中

Java虚拟机中,处理异常不是由字节码指令来实现,而是采用异常表来完成。

指令集中有monitorenter和moniterexit两条指令来支持synchronized(同步)关键字的语义(P259)