什么是JVM
Java 虚拟机是整个 Java 平台的基石,是 Java 语言生成出极小体积的编译代码的运行平台,Java 虚拟机可以看作是一台抽象的计算机,它有自己的指令集以及各种运行时内存区域,它与java语言没有必然的联系,只与二进制文件有所关联,我们知道class文件由java语言编译,class文件中包括字节码(指令集),符号表,及其他辅助信息,既一种不依赖于特定硬件和操作系统的二进制格式,它精确地定义了类与接口的表示形式 ,常以文件形式存在,jvm则接收这样的文件来执行程序。
JVM虚拟机结构
JVM数据类型包括原始类型和基本类型。
- 原始类型:
1.1 整数类型:byte 、short 、int、long、char
1.2 浮点类型:float 、double
1.3 布尔类型:boolean
1.4 returnAddress类型 :returnAddress【表示一条字节码指令的操作码】 - 引用类型:Class Type 、Array Type 、Interface Type
运行时数据区
Java 虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出 而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程开始和结束而创建和销毁。运行时数据区包括 PC寄存器、java虚拟机栈、java堆、方法区、运行时常量池、和本地方法栈.
PC寄存器: 它可以支持多线程执行,一个正在被线程执行的方法被称为当前方法(CurrentMethod),如果这个方法是native(非java的)的 ,PC寄存器保存JVM执行字节码指令的地址。如果不是,则值是undefined。
java虚拟机栈: 每一条 Java 虚拟机线程都有自己私有的 Java 虚拟机栈,它与线程同时创建,用于存储栈帧,和普通的栈一样,符合 “后进先出”原则。 另外,java虚拟机栈中使用的内存不要求是连续的。
栈帧: 用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)。随着方法调用而创建,随着方法结束而销毁,每一个栈帧都有自己的局部变量表、操作数栈和指向当前方法所属的类的运行时常量池的引用,栈帧容量的大小仅仅取决于 Java 虚拟机的实现和方法调用时可被分配的内存。栈帧是线程本地私有的数据,不可能在一个栈帧之中引用另外一条线程的栈帧。
java堆: 堆(Heap)是可供各条线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。存储了受GC(Garbage Collector[垃圾收集器】)管理的各种对象,如果实际内存超出最大内存会抛出OutOfMemoryError异常。
方法区: 供各条线程共享的运行时内存区域。它存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法。如果方法区的内存空间不能满足内存分配请求,抛出OutOfMemoryError异常。
运行时常量池: 每一个运行时常量池都分配在方法区,类和方法被加载后,常量池被创建。当创建类或接口的时候,如果构造运行时常量池所需要的内存空间超过了方法区所能提供的最大值,那 Java 虚拟机将会抛出一个 OutOfMemoryError 异常
本地方法栈: 用来支持非java语言的方法执行。既java中native 标识的方法。如果线程请求分配的栈容量超过本地方法栈允许的最大容量时, Java 虚拟机将会抛出一个 StackOverflowError 异常。 如果本地方法栈可以动态扩展,并且扩展的动作已经尝试过,但是目前无法申请到足够的内存去完成扩展,或者在建立新的线程时没有足够的内存去创建对应的本地方法栈,那 Java 虚拟机将会抛出一个 OutOfMemoryError 异常。
PC Register程序计数器: 指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间。
当我们通过类加载器把类加载进来时,首先存放在方法区中 这里把除了基本数据类型外存储的都是指向堆中的地址,具体的内容都存放在堆中。当我们执行某个方法时,把他压入栈中,再取出执行,每一个方法都会是一个栈帧,递归就是最好的例子,会反复的向栈中压入栈帧,直到返回。方法的执行则交由执行引擎执行,程序计数器指明下一个执行的指令。
jvm调优
要想了解JVM调优相关的知识,首先我们要知道,JVM的运行原理,我们了解我们当每启动一个JAVA应用就会产生一个java虚拟机实例,那么他是怎么运行的呢,首先,我们会把我们编写的.java文件编译为.class的二进制文件,然后JVM经由Class Loader将class文件加载至运行时数据区,之后交由执行引擎进行方法执行。在jdk1.7之前,jvm分为新生代(young)、养老代(old)、以及永久代(permanent) 而新生代有分为伊甸区(eden),和2个存活区(servivor),在1.8中,取消掉了永久区,转而用元空间(metaspace)代替,元空间在本地内存中。我们可以通过非标准参数和非Stable参数命令查看有哪些可以使用的参数。
从这上面两幅图中可以看出我们有大量的参数以供选择,下面就先列出常用的一些参数。
- -XX:PermSize=512M -XX:MaxPermSize=1024M (1.8以前)-XX:MetaspaceSize=512M XX:MaxMetaspaceSize=1024M (** 1.8 **)
- -Xms256m -Xmx512m java堆内存的最大值最小值,一般设定为老年代存活对象的3-4倍。
- -Xss128k:设置每个线程的堆栈大小。
- -XX:NewRatio=4 表示新生代和养老代的比例为1:4。
- -XX:SurvivorRatio=4 表示伊甸区和存活区(默认有2个存活区)的比例为2:4。
- -XX:MaxTenuringThreshold=7 指的是一个对象在存活区移动了7次还未被回收则进入养老代。
- ……等等。 [^ parm_more]
GC垃圾回收
更对内容后续更新