Java运行时数据区域
前言
根据《java虚拟机规范》,Java运行时管理的内存区域包括下图展示的几个运行时数据区域:
运行时数据区主要分为两类,一类是由所有线程共享的数据区域,一类是线程隔离的数据区域。
线程共享的数据区域包含方法区和堆内存。
线程隔离的区域包含虚拟机栈,本地方法栈,以及程序计数器。
接下来会针对这些区域进行一个讲解
一、线程隔离区域?
1、程序计数器
程序计数器是内存中一块非常小的内存空间。程序计数器可以看做是当前线程执行字节码的行号指示器。
周所周知java是一个多个线程抢夺CPU时间片来工作的。我们的线程在中断后恢复的时候需要知道我们字节码已经运行到哪了,从而恢复工作。其实很好理解,就好比我们在爱奇艺上看电视剧,我们不可能一口气全部看完,有时候会关闭,做其他事(好比切换到其他线程上),当我们再次打开爱奇艺看这个剧我们发现并不是调到第一集从头播放,是从上次停止的地方进行的播放(好比,该线程重新获得CPU时间片,我们需要知道当前已经看到哪里了)。
注:爱奇艺只是一个例子,或许爱奇艺并不是这样实现,不要钻牛角尖
由于线程的切换,每个线程都应该在线程恢复执行的时候到执行的位置,因此需要每个线程都有一个独立的程序计数器。各个计数器互不影响,独立存储。称之为线程私有的内存区域。
2、Java虚拟机栈
与程序计数器一样,Java虚拟机栈也是线程私有的。
我们写Java代码的时候完成一个功能,通常要分成很多的函数进行编码。在每个方法执行的时候,我们会在当前线程的Java虚拟机栈中同步创建一个栈帧(共调用10次方法的话,就会创造出10个栈帧出来)。
栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
局部变量表存放编译期可知的Java虚拟机基本数据类型(byte,short,int,long,float,double,boolean,char)以及引用数据类型和returnAddress(returnAddress现在已经很少见了)。
操作数栈进行栈内计算,在助记符中通过store写会到局部变量表中,通过load从局部变量表中读取数据。操作数栈好比是一台独立运行计算的计算机。
栈帧在执行完毕的时候才会被释放掉,因此线程请求的栈深度过长的时候会OOM
思考1:既然我们知道了方法的调用都会对应的创造栈帧出来,那我们平时写递归程序会有怎样的风险
3、本地方法栈
本地方法栈和虚拟机栈类似,区别在于虚拟机栈执行Java方法,本地方法栈执行的是Native方法。
二、线程共享区域
1、Java堆
Java堆是虚拟机管理的内存中最大的一块区域,随着虚拟机的创建而创建。堆中存放对象的实例,我们说的垃圾回收主要也是收集的堆上的内存空间。
如果堆中没有内存完成实例的分配,并且无法扩展时,虚拟机将会抛出OOM。
虚拟机参数-Xmx最大堆内存,-Xms最小堆内存。虚拟机根据这两个参数是可以进行堆内存扩缩容的。
在生产环境中通常是一个服务占据一台机器的,通常不会存在过多其他服务,因此不用进行扩缩容,扩缩容对系统的开销也很大,会影响系统运行效率。通常我们在线上环境会把-Xms和-Xmx设置成同样大小的数值,防止自动扩缩容
2、方法区
方法区和堆一样,是线程共享的内存区域。用于存储被虚拟机加载的类型信息、常亮、静态变量、即时编译器的代码缓存的等数据。
我们通过javap看到的类型,方法等信息都是存放在这个区域(当然存的不是助记符,是对应的编码)
public class com.baway.vcomplete.TestHashMap
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#19 // java/lang/Object."<init>":()V
#2 = Class #20 // com/baway/vcomplete/TestHashMap
#3 = Methodref #2.#19 // com/baway/vcomplete/TestHashMap."<init>":()V
#4 = Class #21 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 LocalVariableTable
#10 = Utf8 this
#11 = Utf8 Lcom/baway/vcomplete/TestHashMap;
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 args
#15 = Utf8 [Ljava/lang/String;
#16 = Utf8 testHashMap
#17 = Utf8 SourceFile
#18 = Utf8 TestHashMap.java
#19 = NameAndType #5:#6 // "<init>":()V
#20 = Utf8 com/baway/vcomplete/TestHashMap
#21 = Utf8 java/lang/Object
{
public com.baway.vcomplete.TestHashMap();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 10: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/baway/vcomplete/TestHashMap;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class com/baway/vcomplete/TestHashMap
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: return
LineNumberTable:
line 12: 0
line 14: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
8 1 1 testHashMap Lcom/baway/vcomplete/TestHashMap;
}
SourceFile: "TestHashMap.java"
参考《深入理解Java虚拟机》第三版 —— 周志明