likes
comments
collection
share

[Java]JVM学习笔记

作者站长头像
站长
· 阅读数 5

JVM学习笔记

在极客时间学习课程时,李智慧老师分享了关于新技术学习方法的经验,叫做“5-20-2”法。

解释一下,就是在学习一项新技术时:

  • 要在5分钟之内了解他是解决了什么问题,相比竞品技术有什么优势;
  • 在20分钟之内,了解这项技术在解决这些问题的时候用了哪些思路,哪些新颖方法;
  • 在2个小时之内,试着做一些demo快速上手。

如果能做到这些,就对这项技术进行一个持续的深挖,如果解决不了这些问题,就及早收手,免得过多浪费时间。

为了试验上述方法的可行性,以JVM为切入点,我开始有意识的训练自己在短时间内对新技术的学习。虽然时间上失败了,用了10个小时,才对JVM有个大概的印象。(效率较低,有待训练),但是这种以终为始,强迫自己“走马观花”的方法,很适合快速掌握新技术的知识框架。

以下就是关于JVM问题点的整理。

JVM主要解决了哪些问题

  • 实现代码的不同平台可移植性
  • 内存的动态分配
  • 垃圾自动回收

JVM如何解决的这些问题

可移植性

Java代码,根据不同平台,编译出对应的机器码;

这部分需要掌握的知识点:

  • 类文件的结构
  • 类加载机制
  • 代码执行机制

注:这部分不展开,后续填充内容。

内存动态分配

这部分需要掌握的知识点:

  • Java代码都有哪些内容需要加载到内存中
  • JVM中都有哪些区域,每个区域的作用是什么
  • 不同区域之间如何联动

问题1:JVM都会加载哪些信息

需要记入内存的内容包括:类、数据类型、实例化的对象,程序执行状态(执行到的位置)等。

问题2:JVM的内存模型(都有哪些区域、作用)

[Java]JVM学习笔记

所有线程共享:

  • 方法区:又称为永久代、元数据区metaSpace(JDK1.8之后)。指代的一个事情。

用于存储虚拟机加载的类信息、常量、静态变量。

  • 静态常量池存储在方法区。
  • 永久代的垃圾收集是和老年代(old generation)捆绑在一起的,因此无论谁满了,都会触发永久代和老年代的垃圾收集。
  • 堆:OOM重灾区

用于存放实例化的对象。

主要包含:
  • 新生代:又包括Eden区域和Survivor区域,From和To区域均属于Survivor区域(还有一种称呼是Survivor0和Survivor1)。

  • 老年代:主要用于“年龄”超过15的对象(转移年龄可设置),以及需要连续内存空间的大对象;

资源分配习惯:
  • Young和Old区域比例为1:2
  • Eden和Survivor0区域比例为8:1 ,这样就可以保障Young新生代10等分。
区域划分的原因:

为了在不同区域执行不同的GC算法,根据不同对象的生命周期,选择不同的GC算法,有利于效率最大化。

对象转移机制:
  • 对象被创建,存储在Eden区;
  • 初次Minor GC后依然存活,转移到Survivor区域;
  • 每次GC 后仍存活,年龄+1;
  • 到达15岁(默认值,参数可设置)之后,转移到Old区;
  • 如果大对象创建时,需要联系内存空间,Eden不满足时直接在Old区域创建。

堆内存划分示意图:

[Java]JVM学习笔记

注:新生代存储中,除了Eden外,叫做Survivor区域;由于Survivor区域采用复制内存回收的方法,所以平均分为两份,一般称为Survivor0和Survivor1,互为内存迁移目的。

所以Eden 和Survivor0的比例设置为8:1 。

线程私有:

  • 虚拟机栈(VM Stack)

描述Java方法执行,每个方法被执行时创建一个“栈针”,

会出现OverOutStack(栈溢出)

主要包含:
  • 操作数栈:
  • 局部变量表:用于记录方法的局部变量,注:是引用,不是真实对象
  • 帧数据区
  • 本地方法栈(Native Method Stack)

该区域主要是为JVM的方法(Native Function)服务。这个区域不用考虑。

  • 程序计数器(Program Counter Register)

JVM中最小的区域,记录当前程序所执行字节码的行号指示器。记录程序执行到的当前位置。

问题3:不同区域之间如何联动

注:待完善

垃圾自动回收(GC)

问题:

  • 都有哪些内存区域需要被回收
  • 怎么判断要不要回收
  • 如何回收内存,常见GC算法
  • 不同区域的GC算法

问题1:都有哪些区域的内存会GC

  • 新生代(Young):堆内存一部分
  • 老年代(Old):堆内存一部分
  • 永久代(MetaSpace):方法区

问题2:如何判断当前对象要不要被回收

传统说是对象引用法。并不准确,会出现两个无用对象互相引用的方法,导致两个对象引用数都大于0,无法被GC。

目前JDK采用:可达性分析,有向图算法,从Root Object连接不到的对象,都被GC。

VM栈中对象可以被理解为Root Object,进行向下搜索。

问题3:常见的GC算法

  • 标记清除算法:标记无用Ob,进行清除。缺点是会产生大量碎片空间。
  • 标记整理压缩算法:对对象进行标记,进行内存区域整理合并;
  • 复制算法:两个相同的区域,将区域1中内存整理后,全部放置在区域2中;
  • 分代收集算法。

问题4:不同区域GC算法

Eden:问题点。

Survivor:标记-复制算法

Old & MetaSpace:一般使用CMS收集算法

Full GC:Old区或者MetaSpace满了之后,会触发,导致JVM全部暂停。Stop the world

问题5:垃圾收集器概念

Serial:

ParNew:

Parallel:

CMS:并发-标记-清除算法,以牺牲吞吐量来换取最短回收停顿时间的算法。(分线程进行标记清理)

G1:并行标记,分代收集,空间整理,可预测停顿。(没搞懂,待定)

注:当Old区域的剩余空间小于平均大小,就会触发Full GC,又称为“Stop the world”。整个JVM全部停止,完成内存资源回收。Old区域GC资源消耗大,应该尽量避免生命周期较短的大对象。

使用的工具

JVM配置项

内存区域大小配置:

通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区(永久代)的大小。

-XX:+UseConcMarkSweepGC:设置CMS GC算法

-Xss:设置栈内存
-Xmn:设置年轻代内存,(Eden + Survivor)
-XX:SurvivorRatio=8:设置Eden和Survivor比例为8:1
-XX:PretenureSizeThreshold:设置大对象阈值,超过这个值就放置在Old区域

-Xms:堆内存的最小大小,默认为物理内存的1/64

-Xmx:堆内存的最大大小,默认为物理内存的1/4

-Xmn:堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn

GC配置:

设置OOM时打印dump文件。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=file_path

修改GC收集器为cms
-XX:+UseConcMarkSweepGC

垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails:console显示GC详细信息
-XX:+PrintGCTimeStamps
-Xloggc:filename

OOM

堆内存溢出

java.lang.OutOfMemoryError: Java heap space

  • 要根据Dump文件,判断哪个区域内存溢出,相应的修改代码逻辑,查看是否有废弃对象,未放开引用。
  • 适当调整JVM参数。

栈内存溢出

java.lang.OutOfMemoryError.StackOverflowError

程序所要求的栈深度过大导致,可以写一个死递归程序触发。

永久代溢出

OutOfMemoryError: PermGen space

持久带中包含方法区,方法区包含常量池。

因此持久带溢出有可能是(1) 运行时常量池溢出,也有可能是(2)方法区中保存的Class对象没有被及时回收掉或者Class信息占用的内存超过了我们配置。

注:实战出真知,理论学习都是入门。

无法创建本地线程

Caused by: java.lang.OutOfMemoryError:unable to create new native thread

系统内存的总容量不变,堆内存、非堆内存设置过大,会导致能给线程分配的内存不足。

常用工具

JVM分析工具

  • Jps:查看当前Java进程,及进程号
  • 使用jmap工具分析
C:\Users\Yanru>jmap -J-d64 -heap 30284(PID)
Attaching to process ID 30284, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.201-b09
using parallel threads in the new generation.  ##新生代采用的是并行线程处理方式
using thread-local object allocation.  
Concurrent Mark-Sweep GC   ##同步并行垃圾回收
Heap Configuration:  ##堆配置情况,也就是JVM参数配置的结果[平常说的tomcat配置JVM参数,就是在配置这些]
   MinHeapFreeRatio = 40 ##最小堆使用比例
   MaxHeapFreeRatio = 70 ##最大堆可用比例
   MaxHeapSize      = 2147483648 (2048.0MB) ##最大堆空间大小
   NewSize          = 268435456 (256.0MB) ##新生代分配大小
   MaxNewSize       = 268435456 (256.0MB) ##最大可新生代分配大小
   OldSize          = 5439488 (5.1875MB) ##老年代大小
   NewRatio         = 2  ##新生代比例
   SurvivorRatio    = 8 ##新生代与suvivor的比例
   PermSize         = 134217728 (128.0MB) ##perm区 永久代大小
   MaxPermSize      = 134217728 (128.0MB) ##最大可分配perm区 也就是永久代大小
Heap Usage: ##堆使用情况【堆内存实际的使用情况】
New Generation (Eden + 1 Survivor Space):  ##新生代(伊甸区Eden区 + 幸存区survior(1+2)空间)
   capacity = 241631232 (230.4375MB)  ##伊甸区容量
   used     = 77776272 (74.17323303222656MB) ##已经使用大小
   free     = 163854960 (156.26426696777344MB) ##剩余容量
   32.188004570534986% used ##使用比例
Eden Space:  ##伊甸区
   capacity = 214827008 (204.875MB) ##伊甸区容量
   used     = 74442288 (70.99369812011719MB) ##伊甸区使用
   free     = 140384720 (133.8813018798828MB) ##伊甸区当前剩余容量
   34.65220164496263% used ##伊甸区使用情况
………………
  • jmap -dump:format=b,file=idea.bin 22128

生成22128进程的JVM状态文件。

  • jstat:查看本地类和远端JVM的运行状况

在没有可视化终端排查问题的首选。针对13224进程,没250毫秒查询一次,查询20次。

C:\Users\Yanru>jstat -gc 13224 250 20
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
10240.0 10240.0  0.0    0.0   81920.0  35945.2   102400.0     0.0     4480.0 775.8  384.0   76.4       0    0.000   0      0.000    0.000
  • jinfo UID:查看线程的JVM属性

VM Flags: Non-default VM flags: -XX:CICompilerCount=4 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=null -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=52428800 -XX:MaxNewS …………

  • jmc:可视化JVM检测工具

[Java]JVM学习笔记

  • jconsole:Java监视管理控制台

可以查看堆内存各区域大小。

[Java]JVM学习笔记

JVM监控工具

  • Jconsole:可以查看本地的Jvm dump文件

  • VisualVM:大一统的可视化监控工具

[Java]JVM学习笔记

JVM调优策略

<待更新>

常见面试题

<待更新>

当然,上述这项提纲性质的内容,对JVM这项庞大且应用广泛的技术,恐怕连入门都算不上,最多算是自己理解这项技术的技能树,后续遇到问题,再在对应的区域填充知识。

这个思维架构类似于《神探夏洛克》中的记忆宫殿