文章目录
JVM内存结构
1、运行时数据区域
运行时数据区域的定义是:java虚拟机在执行java的过程当中会把它管理的内存划分为若干个不同的区域。
在JVM中、JVM的内存区域主要分为堆、方法区 、程序计数器、虚拟机栈、本地方法栈。
按照与线程的关系也可以分为
线程私有:每个线程单独拥有的一片内存区域
线程共享:被所有线程共享,只有一份
1.1线程私有
每个线程单独拥有的一片内存区域
1.1.1、程序计数器
程序计数器是当前线程执行字节码指令的行号指示器,各个线程之间相互独立,互不影响。
主要用来记录各个线程执行的字节码的地址, 例如, 分支、 循环、 跳转、 异常、 线程恢复等都依赖于计数器 。
如果线程执行的是java的一个方法,那么程序计数器指向的是当前线程执行代码的字节码行数。
如果线程执行的是native方法,那么程序计数器的值为空。(执行native方法不是JVM执行,在操作系统也有相对应的程序计数器来记录本地代码的地址)
为什么会存在程序计数器:
cpu在执行指令时在一个确定的时候只会执行一条指令,一个线程当中会存在多个指令,而执行的线程数量超过cpu数量时,线程之间会根据时间片轮询机制进行争夺cpu资源,如果一个线程分配的时间用完了或者其他原因导致这个线程的cpu资源被抢夺,为了在下次该线程再次获得cpu资源时能恢复到正确的执行位置,那么该线程就需要一个程序计数器用于记录下一条运行的指令地址或行号。
1.1.2、虚拟机栈 -Xss
栈(Stack) 作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表,遵循先进后出。先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
虚拟机栈是描述java方法执行的内存模型,线程在运行时每个方法都会被打包成一个栈帧,用于存储局部变量表、操作数栈、动态链接、完成出口等信息。然后将打包的栈帧放入到虚拟机栈中。而当前执行的方法就是虚拟机栈顶的栈帧。方法执行就对应着栈帧在虚拟机栈中入栈和出栈的过程。
在编译程序代码的时候, 栈帧中需要多大的局部变量表, 多深的操作数栈都已经完全确定了, 并且写入到方法表的 Code 属性之中, 因此一个栈帧需要分配多少内存, 不会受到程序运行期变量数据的影响, 而仅仅取决于具体的虚拟机实现 。
栈的缺省大小在JDK1.5以后默认为1M ,可以通过JVM参数 -Xss调整大小,如-Xss2M。
栈帧 :在每个 Java 方法被调用的时候,都会创建一个栈帧,并入栈。 一旦方法完成相应的调用,则出栈。
栈帧大体包含四个区域:局部变量表、操作数栈、动态链接、完成出口。
1、局部变量表
局部变量表用于存放方法当中的局部变量
2、操作数据栈
存放我们方法执行的操作数的
3、动态链接
既然是执行方法, 那么我们需要知道当前栈帧执行的是哪个方法, 栈帧中会持有一个引用(符号引用) , 该引用指向某个具体方法 。持有这个引用就是为了支持方法调用过程中的动态连接
4、完成出口
正常返回(调用程序计数器中的地址作为返回)
异常的话(通过异常处理器表<非栈帧中的>来确定)
1.1.3、本地方法栈
本地方法栈与虚拟机栈发挥的作用十分相似,区别是虚拟机栈是用于执行java方法,而本地方法栈是执行native方法。
1.2、线程共享
1.2.1、方法区
方法区是用来存放已被虚拟机加载的类的相关信息。包括类信息,静态变量,常量、运行时常量池,字符串常量池等。
方法区是JVM的逻辑分区,在JDK1.7及以前称为永久代。在JDK1.8及以后称为元空间。
而当类加载到内存中后, JVM 就会将 class 文件常量池中的内容存放到运行时的常量池中; 在解析阶段, JVM 会把符号引用替换为直接引用 。
常量池有很多概念, 包括运行时常量池、 class 常量池、 字符串常量池。
虚拟机规范只规定以上区域属于方法区, 并没有规定虚拟机厂商的实现。
严格来说是静态常量池和运行时常量池:
静态常量池是存放字符串字面量、 符号引用以及类和方法的信息。
运行时常量池存放的是运行时一些直接引用。运行时常量池是在类加载完成之后, 将静态常量池中的符号引用值转存到运行时常量池中, 类在解析之后, 将符号引用替换成直接引用。
在 HotSpot 虚拟机、 Java7 版本中已经将永久代的静态变量和运行时常量池转移到了堆中, 其余部分则存储在 JVM 的非堆内存中(逻辑上还是属于方法区 ), 而 Java8 版本已经将方法区中实现的永久代去掉了, 并用元空间(class metadata) 代替了之前的永久代, 并且元空间的存储位置是本地内存
方法区的JVM参数设置:
jdk1.7及以前 永久代:
-XX:PermSize:初始值
-XX:MaxPermSize: 最大致
jdk1.8及以后 元空间:
-XX:MetaspaceSize:初始值
-XX:MaxMetaspaceSize : 最大值
1.2.2、堆
堆(Heap)是JVM 上最大的内存区域, 我们创建的几乎所有的对象, 都是在这里存储的。
也是垃圾收集器进行垃圾收集的内存区域 。
堆空间一般在程序启动时就申请了,但并不一定会全部使用。
随着对象的频繁创建,堆空间的占用会越来越多,就需不定期对对不使用的对象进行回收。在java中,就叫做GC(Garbage Collection )
堆的JVM参数设置:
-Xms: 堆的最小值;
-Xmx: 堆的最大值;
-Xmn: 新生代的大小;
-XX:NewSize; 新生代最小值;
-XX:MaxNewSize: 新生代最大值;
-XX:SurvivorRatio: eden区与survivor区的比值。设置的值为多少就表示eden区占几份 survivor区固定2份(form/to各占一份),总份数为设置的值+2
新生代
eden
survivor to
survivor form
老年代
2、直接内存
操作系统上的除去被JVM占用的其他内存 ,剩余的物理内存