目录
- jvm命令
- jvm的运行时数据区
- jvm会发生哪些ERROR
- 从一个class出发理解数据区
jvm命令
JVM参数类型
- 标准: 稳定的,长期没有变化
- X: 相对变化较少的
- XX: 变化较大,JVM调优重点
设置参数时,idea指定在VM options里面,命令行直接加在java命令后
java -Xss10m -XX:+PrintGCDetails JVMParams |
常见的XX类型的参数
-XX:+PrintGCDetails: 打印GC日志
-XX:+PrintFlagsInitial: 打印所有初始的参数信息
-XX:+PrintFlagsFinal: 打印所有最终的参数信息
-Xms设置堆的最小空间大小。
-Xmx设置堆的最大空间大小。
-XX:NewSize设置新生代最小空间大小。
-XX:MaxNewSize设置新生代最大空间大小。
-XX:PermSize设置永久代最小空间大小。
-XX:MaxPermSize设置永久代最大空间大小。
-Xss设置每个线程的堆栈大小。
没有直接设置老年代的参数,但是可以设置堆空间大小和新生代空间大小两个参数来间接控制
老年代空间大小=堆空间大小-年轻代大空间大小
例如:
java -XX:+PrintFlagsFinal -version
... |
上面只显示部分参数,但是能够说明我们需要理解的内容,即 ‘=’ 表示默认值,‘:=’ 表示被修改过的值。同时还有数值类型和布尔类型。
几个特殊的XX类型参数
-Xms、-Xmx、-Xss 实际上是XX类型的缩写
-Xms ==> -XX:InitialHeapSize: 表示为: -Xms10m
-Xmx ==> -XX:MaxHeapSize: 表示为: -Xmx10m
-Xss ==> -XX:ThreadStackSize
常用命令
查看java进程:jps
[hadoop ~]$ jps
13612 JVMParams
13644 Jps查看java进程的参数信息
jinfo -flag name pid[hadoop13612 ~]$ jinfo -flag MaxHeapSize
-XX:MaxHeapSize=2048917504
[hadoop13612 ~]$ jinfo -flag InitialHeapSize
-XX:InitialHeapSize=130023424
[hadoop13612 ~]$ jinfo -flag ThreadStackSize
-XX:ThreadStackSize=1024怎么理解-XX:MaxHeapSize=2048917504,-XX:InitialHeapSize=130023424 ?
分析:
我主机的物理内存为8G,2048917504k = 1.9G,130023424 = 124M
理论上heap的最大值为物理内存的1/4,最小值为物理内存的1/64
但是一般情况下,我们会把MaxHeapSize和InitialHeapSize设置相同的值,防止内存抖动
查看java进程的默认和设置的参数
jinfo -flags pid
[hadoop13612 ~]$ jinfo -flags
Non-default VM flags: -XX:CICompilerCount=2 ...
Command line: -Xss10m -XX:+PrintGCDetails ...
jvm的运行时数据区
程序计数器
程序计数器是每个线程私有的
程序计数器是一块较小的内存空间,它可以看做是当前线程的行号指示器,这在多线程环境下非常有用。使得线程切换后能够恢复到正确的执行位置。
在java虚拟机规范中,这是唯一一个没有规定任何OutOfMemoryError的地方
Java虚拟机栈
java虚拟机栈也是每个线程私有的,它的生命周期和线程相同
虚拟机栈描述的是java方法执行的线程内存模型: 每个方法被执行的时候,java虚拟机栈都会同步创建一个Frame(栈帧)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直到执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
最常用的就是局部变量表,局部变量表存放了编译期可知的各种java虚拟机的基本数据类型(boolean、byte、char、sort、int、long、float、double)、对象引用(reference类型)和returnAddress类型,这部分在后面讲有详细的解释。
在java虚拟机规范中,这个区域可能存在两种异常,如果线程请求的栈深度大于虚拟机所允许的深度,会抛出StackOverflowError异常,常见的有循环调用方法名;如果java虚拟机栈容量可以动态扩展,当栈无法申请到足够的内存时就会抛出OutOfMemoryError异常。
本地方法栈
本地方法栈是线程私有的
本地方法栈与虚拟机栈所发挥的作用是非常类似的,其区别只是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则为本地(native)方法服务
常见的本地方法有getClass、hashCode、clone、notify、notifyAll、wait、sleep等
与java虚拟机栈一样,本地方法栈也会在栈深度溢出的时候或者栈扩展失败的时候抛出StackOutflowError和OutOfMemoryError异常。
java堆
java堆是所有线程共享的,是虚拟机所管理的内存中最大的一块,在虚拟机启动时创建。
此内存区域的唯一目的是存放对象实例,对象实例包括对象和数组。
如果在java堆中没有内存完成实例分配,并且堆也无法扩展,java虚拟机将会抛出OutOfMemoryError异常。
方法区
方法区是所有线程共享的
它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据,简单点说就是Class。
方法区只是java虚拟机的规范,它属于堆的一个逻辑部分,为了和堆区分开,也叫非堆。在jdk8之前,方法区的具体实现叫做永久代,
- 由于类及方法的信息大小很难确定,所以内存设置小了会发生OOM,设置大了又浪费
- GC复杂度高,回收效率低
- 合并 HotSpot 与 JRockit
所以在jdk8完全用元空间替换了永久代,元空间直接使用的系统内存。
在java虚拟机规范中,如果方法区无法满足内存分配需求时,会抛出OouOfMemoryError
直接内存
直接内存不是java虚拟机规范中定义的内存区域,但是这部分也会被频繁使用,所以也可能会抛出OutOfMemoryError。
jvm会发生哪些ERROR
java堆内存OOM异常测试
-Xmx10m -Xms10m -XX:+HeapDumpOnOutOfMemoryError |
运行结果:
java.lang.OutOfMemoryError: Java heap space |
java虚拟机栈和本地方法栈SOF测试
-Xss2M |
运行结果:
栈深度:41075 |
java虚拟机栈和本地方法栈OOM测试
-Xss4m |
运行结果:
死机
方法区和运行时常量池OOM
-XX:PerSize=6m -XX:MaxPermSize=6M |
运行结果:
jdk8没有测试出来
从一个class出发理解数据区
如图所示,很容易的理解各区分别保存java代码中的哪些部分
堆区: 保存的People对象
栈区: 保存的栈帧,栈帧中保存了引用name和age和引用people
方法区: 保存的People.class相关的,包括类型信息、常量、静态变量sss
运行时常量池: 保存name和age字符串内容