JVM(一):JVM运行时数据区域

几个简单概念

这里直接引用了五月的仓颉的博客原文。

http://www.cnblogs.com/xrq730/p/4827590.html

1、计算机存储单位

从小到大依次为Bit字节Byte千字节KBM千兆GBTB,相邻单位之间都是1024倍,1024210次方,即:

· 1Byte = 8bit

· 1K = 1024Byte

· 1M = 1024K

· 1G = 1024M

· 1T = 1024G

2、计算机存储元件

寄存器:中央处理器CPU的一部分,是计算机中读写速度最快的存储元件,但是容量很少

内存:属于独立的一个部件,是和CPU沟通的桥梁,用于存放CPU中的运算数据以及与外部存储器交换的数据。尽管在今天,对内存的读写速度已经很快了,但是由于寄存器是在CPU上的,所以对于内存的读写速度和对于寄存器的读写速度上还是有几个数量级的差距。但是没办法,对于内存的读写I/O操作是很难消除的,寄存器数量有限,不可能通过寄存器来完成所有的运算任务

3、内核空间和用户空间

连接内存和寄存器的是地址总线,地址总线的宽度影响了物理地址的索引范围,因为总线宽度决定了处理器一次可以从寄存器或内存中获取多少个Bit,同时也决定了处理器最大可以寻址的地址空间。比如32位CPU的系统,可寻址范围为0x00000000~0xFFFFFFFF,即232=4294967296个内存位置,每个内存位置1个字节,即32位CPU系统可以有4GB的内存空间。

不过应用程序是不可以完全使用这些地址空间的,因为这些地址空间被划分为了内核空间和用户空间,程序只能使用用户空间的内存。

内核空间主要是指操作系统运行时所使用的用于程序调度、虚拟内存的使用或者链接硬件资源的程序逻辑。

区分内核空间和用户空间的目的主要是从系统的稳定性的角度考虑的。Windows 32操作系统默认内核空间和用户空间的比例是1:1,即2G内核空间、2G内存空间,32位Linux系统中默认比例则是1:3,即1G内核空间,3G内存空间。

4、字长

CPU的主要技术指标之一,指的是CPU一次能并行处理二进制的位数(Bit)。通常称处理字长为8位数据的CPU为8位CPU,32位CPU就是在同一时间内处理字长为32位的二进制数据。不过目前虽然CPU大多是64位的,但还是以32位字长运行

 

运行时数据区域

参考周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践》

很多地方直接引用原文,文中写得很好。


参考上面图片,JVM所管理的内存包括上面几个运行数据区域

 

1. 程序计数器

它是一块较小的内存空间,可以被看作是当前线程所执行的字节码的行号解释器。

字节码解释器选取下一条需要执行的字节码指令和分支、循环、跳转、异常处理和线程恢复等基础功能都需要这个计数器来完成。

不同的线程有各自独立的计数器,独立存储。在线程切换执行的时候互不影响。如果线程执行的是一个Java方法,那么计数器记录的是虚拟机字节码指令的地址;如果执行native的本地方法,那么计数器的值为undifined

2. Java虚拟机栈

虚拟机栈是线程私有的,并且每个方法执行的时候都会创建一个栈帧,用该栈帧来存储局部变量表操作数栈、动态链接和方法出口等信息

局部变量表存放了基本数据类型、对象的引用(可能是对象地址的引用指针或者是句柄)和returnAddress类型(指向字节码指令的地址)。

局部变量表的内存空间是在编译期间完成分配的,进入一个方法需要在帧中分配多大的局部变量空间是已经确定好的。不会在运行时发生改变。

3. 本地方法栈

虚拟机栈是虚拟机为虚拟机执行Java方法的服务。

本地方法栈是为虚拟机执行native方法的服务。

4. 

堆是JVM管理的内存最大的一块,在虚拟机启动的时候创建。它被所有的线程所共享并且是GC管理的主要区域,所以又叫“GC堆”。

对象的实例创建都在堆中分配内存。

 

5. 方法区

方法区跟堆一样是线程共享的内存区域,又叫“永久代”,是存储被虚拟机加载的类信息常量静态变量、即时编译器编译后的代码等数据。

 

6. 运行时常量池

运行时常量池是方法区的一部分,存放的是编译期生成的各种字面量和符号引用。运行时常量池有个重要的特征就是动态性,具体看下面的例子:

 

@Test
    public void testDHObject(){
        String s1 = "dh";
        String s2 = "dh";

        String s3 = new String("dh");

        System.out.println("s1 == s2 ?" + (s1==s2));
        System.out.println("s1 == s3 ?" + (s1==s3));

        System.out.println("动态加入常量池:s1==s3?" + (s1 == s3.intern()));
    }

 运行结果:

s1 == s2 ?true
s1 == s3 ?false
动态加入常量池:s1==s3?true

 

intern()方法在运行期间将新的常量放入池中。


还有个直接内存,它不是运行时数据区的一部分,这里不讲解。

这个也比较重要,希望读者可以自行查阅。

 

猜你喜欢

转载自blog.csdn.net/Adrian_Dai/article/details/80228747