JAVA JVM运行时数据区详解
作者:苍何fly
一、前言
这是JVM系列文章的第三篇,这篇文章将对整个JVM运行时数据区和GC垃圾回收详细的介绍。这部分也算是JVM的核心内容了。
二、运行时数据区整体概架构
以下是自己的一句话总结:
分为线程私有和线程共享的两大类,其中程序计数器、虚拟机栈、本地方法栈是属于线程私有的,堆内存及方法区内存是线程共享的。程序计数器主要是记录字节码指令,CPU上下文切换线程,从一个线程切换到另一个线程,需要知道线程执行到哪一步,所以记录这个指令就是很有必要的,程序计数器无OOM和GC的发生。虚拟机栈里面是一个个栈帧,每一个栈帧对应着每一个方法,栈帧又是由局部变量表、操作数栈、方法返回值地址、动态链接组成。虚拟机栈可能会发生栈溢出异常,即starkoverflow本地方法栈是存放本地方法相关的东西;堆是一块很大的空间,整体分为2大块,新生代和老年代,新生代又分了Eden区、S0区、S1区,垃圾回收主要发生在新生代,每一个区对应不同的垃圾回收算法;方法区保存的是一些常量、类的基本信息等,方法区对应的实现在JDK7中是永久代,在JDK8中是元空间。
三、程序计数器
用来储存指向下一条指令的地址,是线程私有的,生命周期和线程的生命周期一致。
四、虚拟机栈
虚拟机栈是线程私有的,内部保存一个个栈帧,每一个栈帧对应一个Java方法的调用,生命周期和线程的生命周期保持一致。先来看看栈的特点。
1、栈的特点
栈是运行时的单位,而堆是存储的单位。栈的特点是先进后出,后进先出。
可以通过参数-Xss来设置栈空间大小
2、栈帧的内部结构
3、局部变量表
是一个数字数组,主要用于存储方法参数和定义在方法内的局部变量,这些数据类型包括各类基本数据类型,对象引用等,所需的容量大小是在编译期确定下来的,在方法运行期间是不会改变局部变量表大小的。
关于Slot的理解:
静态变量和局部变量的区别:
总结:
在栈帧中,与性能关系最为密切的就是局部变量表,在方法执行时,虚拟机使用局部变量表完成完成方法的传递,局部变量表中的数据也是可达性分析中的GC Root,如果一个对象在局部变量表中还有引用,那么根绝可达性分析算法,这个变量就不属于垃圾对象,是不会被GC回收的。
4、操作数栈
操作数栈是栈中栈,也可称为表达式栈,在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈和出栈。主要用于保存计算过程的中间结果。操作数栈,可以看成是临时寄存器,计算过程中变量的临时保存
5、动态链接
方法重写的本质
6、方法返回地址
存放调用该方法的PC寄存器的值
五、本地方法栈
管理本地native本地方法,是线程私有的,所谓的本地方法,其实就是一些非Java语言写的代码,这部分代码甚至可以和操作系统CPU进行打交道。
六、堆
堆是内存管理的核心区域,是线程共享的,属于JVM级别,也就是一个JVM实例就会有一个堆空间,注意的是虽然堆整体上是线程共享的,但是在内部有一小块空间是线程私有的缓存区TLAB。
几乎所有的对象实例都是在堆中,堆是GC垃圾回收的重点区域。堆整体可以分为新生代和老年代,新生代又分为Eden区和S0和S1区。
新生代和老年代的比例是1:2,Eden区和s0,s1区所占空间比例是8:1:1
1、设置堆大小的参数
-Xms
:用于表示堆区的起始内存,默认情况下,占物理内存大小的64分之一。
-Xmx
用于表示堆区的最大内存,默认情况下,占物理内存的四分之一。
通常起始内存和最大内存两个参数设置成一样,目的是为了GC清理完堆区内存后不需要重新分隔 计算堆区的大小,从而提高性能。 查看设置的参数: 方式一:jps(查看进程) jstat -gc 进程id 方式二:-xx:+printGCDetails
2、对象分配过程
这里s0和s1谁是空的谁就是to,年龄计数器阈值是15,YGC是在Eden区满的时候会触发,s0和s1满的时候不会触发YGC,YGC会将s区以及伊甸园区一起GC
关于垃圾回收,频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。
Visualvm是JVM常用调优工具,在JDK的bin下就可以打开
3、堆中的GC
年轻代(Minor GC)触发机制
老年代GC(Major GC/Full GC)触发机制
Full GC 触发机制
4、内存分配策略
5、什么是TLAB
TLAB表明堆不一定是共享的。
6、堆是分配对象存储的唯一选择吗?
如果经过逃逸分析,一个对象并没有逃逸出方法的话,那么就有可能被优化成栈上分配。
逃逸分析手段:
注意:JDK6U23版本后,HotSpot默认已经开启逃逸分析。所以我们得出一个结论,开发中能使用局部变量的,就不要使用在方法外定义。JDK7后字符串常量池和静态变量存储在堆中
七、方法区
方法区可以看做是一块独立于堆的内存空间,是线程共享的,主要存储类信息、运行时常量池等,也会发生OOM,JDK8前成为永久代,JDK8成为元空间。(元空间和永久代最大的区别是,元空间不再使用JVM内存,而是使用了本地内存技术)
1、方法区概述
2、设置方法区内存大小
3、如何解决OOM问题?
4、方法区存储什么
5、方法区的演进细节
6、方法区的GC
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!