今天是1024,祝愿各位程序员节日快乐!
书归正传,在JAVA并发编程中,程序员对于JVM的了解程度也一部分决定了并发代码的质量,因此于JVM就显的格外重要,下面就和大家谈一谈JVM的内存结构和JVM的内存模型(JMM)。
①JVM内存结构
对于java程序员来讲,由于java代码运行时的内存是交由JVM去处理的,所以一旦出现内存泄漏或者内存溢出的问题,对JVM内存结构不熟悉的同志可能就没有办法去处理响应的问题了。介绍JVM的内存结构的过程是非常枯燥无味的,学起来也是枯燥的,但是对于大家有些知其然,并不知其所以然的来说,还是十分有帮助和必要的。下面我就给大家按照jdk1.8为例来画一下JVM的内存结构示意图。
程序计数器
程序计数器(Program Counter Register)是JVM中一块较小的内存区域,保存着当前线程执行的虚拟机字节码指令的内存地址。Java多线程的实现,其实是通过线程间的轮流切换并分配处理器执行时间的方式来实现的,在任何时刻,处理器都只会执行一个线程中的指令。在多线程场景下,为了保证线程切换回来后,还能恢复到原先状态,找到原先执行的指令,所以每个线程都会设立一个程序计数器,并且各个线程之间不会互相影响,程序计数器为"线程私有"的内存区域。
虚拟机栈
虚拟机栈(Java Virtual Machine Stacks)和线程是紧密联系的,每创建一个线程时就会对应创建一个Java栈,所以Java栈也是"线程私有"的内存区域,这个栈中又会对应包含多个栈帧,每调用一个方法时就会往栈中创建并压入一个栈帧,栈帧是用来存储方法数据和部分过程结果的数据结构,每一个方法从调用到最终返回结果的过程,就对应一个栈帧从入栈到出栈的过程。
虚拟机栈是一个后入先出的数据结构,线程运行过程中,只有一个栈帧是处于活跃状态的,被称为"当前活动帧栈",当前活动帧栈始终是虚拟机栈的栈顶元素。
本地方法栈
本地方法栈(Native Method Stack)和虚拟机栈的作用相似,不过虚拟机栈是为Java方法服务的,而本地方法栈是为Native方法服务的。
方法区
方法区(Method Area)是用于存储类结构信息的地方,包括常量池、静态变量、构造函数等类型信息,类型信息是由类加载器在类加载时从类文件中提取出来的。
方法区同样存在垃圾收集,因为用户通过自定义加载器加载的一些类同样会成为垃圾,JVM会回收一个未被引用类所占的空间,以使方法区的空间达到最小。
方法区中还存在着常量池,常量池包含着一些常量和符号引用(加载类的连接阶段中的解析过程会将符号引用转换为直接引用)。
方法区是线程共享的。
堆
堆(heap)是存储java实例或者对象的地方,是GC的主要区域,同样是线程共享的内存区域。
以上对于JVM内存结构做了一个详细的分析。
②知道了JVM内存结构,那么JVM的内存模型是什么呢?
Java内存模型看上去和Java内存结构(JVM内存结构)差不多,很多人会误以为两者是一回事儿,这也就导致面试过程中经常答非所为。
在前面的关于JVM的内存结构的图中,我们可以看到,其中Java堆和方法区的区域是多个线程共享的数据区域。也就是说,多个线程可能可以操作保存在堆或者方法区中的同一个数据。这也就是我们常说的“Java的线程间通过共享内存进行通信”。
Java内存模型是根据英文Java Memory Model(JMM)翻译过来的。其实JMM并不像JVM内存结构一样是真实存在的。他只是一个抽象的概念。JSR-133: Java Memory Model and Thread Specification中描述了,JMM是和多线程相关的,他描述了一组规则或规范,这个规范定义了一个线程对共享变量的写入时对另一个线程是可见的。
那么,简单总结下,Java的多线程之间是通过共享内存进行通信的,而由于采用共享内存进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM就是围绕着多线程通信以及与其相关的一系列特性而建立的模型。JMM定义了一些语法集,这些语法集映射到Java语言中就是volatile、synchronized等关键字。JVM在java的并发编程中是非常重要的。
上述分别介绍了JVM的内存结构和JVM的内存模型,这些都是程序员在面试的时候老生常谈的问题,希望大家能正确区分和理解,并加以运用到实际工作中。最后,欢迎大家阅读我的文章,觉得当中说的点不对的还望多多提醒!