JVM内存结构
注:所有内容基于JDK1.8版本。
一、运行时数据区
在了解JVM内存结构前,先了解一个规范——运行时数据区。查看官方解释:Java虚拟机定义了在程序执行期间使用的各种运行时数据区域。其中一些数据区域是在Java虚拟机启动时创建的,仅在Java虚拟机退出时销毁。其他数据区域是每个线程。线程数据区域是在线程退出时创建和销毁线程时创建的。(按照个人理解,就是JVM在执行Java程序时,会把内存划分为不同的数据区域)。
运行时数据区:运行时数据区,其包含:堆、方法区,虚拟机栈、本地方法区、程序计数器。其中堆、方法区是所有线程共享的数据区。虚拟机栈、本地方法区、程序计数器是线程隔离的数据区域。
程序计数器:JVM支持多线程同时执行,每个线程都有自己的程序计数器,线程正在执行的方法的叫当前方法,如果是Java代码,程序计数器存放的是当前正在执行指令的地址。如果执行的是Native方法,这个计数器值为空。可以把它看做是当前线程所执行的字节码的行号指示器。为了防止多线程在CPU之间的切换,线程间相互独立,所有程序计数器是线程隔离的。该内存区域没有规定任何的OutOfMemoryError情况的区域。
虚拟机栈:线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成的过程就对应一个栈帧在虚拟机栈中入栈到出栈的过程。
本地方法栈:与虚拟机栈所发挥的作用非常相似,不同的是虚拟机栈为虚拟机执行Java方法服务。而本地方法栈则为虚拟机使用到的Native方法服务。
堆:Java虚拟机管理内存中最大的一部分。堆是被所有线程共享的一块内存区域,再虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆可以处于物理上不连续的内存空间中,只要逻辑上是联系的即可。
方法区:与堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在方法区中,有个运行时常量池,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放。
二、JVM内存结构
整个JVM的内存分为两大块,一块为堆区,另外一块为非堆区。
堆区又分为两大块,一块叫Young区,一块叫Old区。Young区又分为两大块,一块叫S区(由From Survivor区、To Survivor区。这两个区域空间大小一样。在同一个时间点上,只有一块区域有数据,另外一个为空的)及Eden区组成。
非堆区(Metaspace):非堆区里又分为好几个部分。其中有一块区域叫CCS区,也就是压缩类空间。这个区域只有启用了短指针才存在,否则不存在。在堆里分配的每一个对象,它都会有有一个指向自己class的一个指针。可以将64位的指针压缩成32位指针节省空间。CodeCache存放的是我们JIT编译后的本地代码还有JNI的一些C代码.
总结
Metaspace=Class、Package、Method、Filed、字节码、常量池、符号引用等。
CCS:32位短指针。
CodeCache:JIT编译后的本地代码还有JNI的一些C代码。
常用调整参数
-Xmx
, 堆内存大小的上限。
-Xms
, 堆内存大小的初始值
-XX:NewSize:
新生代预估堆内存占用的默认值
-XX:MaxNewSize:
新生代占整个堆内存的最大值
-XX:
SurvivorRatio Survivor跟Eden区比例。
-XX:
MetaspaceSize、-XX:MaxMetaspaceSize
:设置Metaspace大小。
-XX:CompressedClassSpaceSize
: 类指针压缩空间大小, 默认为1G
-XX:InitialCodeCacheSize、-XX:ReservedCodeCacheSize设置CodeCache大小
JVM参数官网链接:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html#BABHDABI