JVM memory partition and the Java memory model (Java Memory Model)

concept

  • JVM particular memory partition is partition running a JVM data zone.
  • JMM is a specification is an abstract concept, in order to address multi-threaded programming due to communicate via shared memory, a local memory data inconsistency exists, the compiler will reorder code instructions, the processor will execute the code reordering problems caused, which is to ensure the correctness of shared memory (visibility, order, atomicity).
  • Java memory partition and JMM concept is completely different levels, said JMM more appropriate description is a set of specifications around the atomicity, ordering, visibility, this set of specifications through the control program of each variable in the shared data area and private data access method area. JMM Java Memory areas and actually are abstract concepts, the only similarities, there is a shared data area and private data area. JMM in the main memory are shared data area, from a certain extent, it should include a heap and method area, and working memory data thread private data area, from a certain extent, it should include a program counter, stack and local virtual machine methods stack. Perhaps in some places, we may see the main memory is described as a heap memory, working memory is called thread stack, in fact they are the expression of the same meaning.

JVM memory zoning

Here Insert Picture Description
A common problem: .java files are compiled to Java Compiler .class bytecode files, and then Class loader to load the bytecode files of various types, loading handed over Execution Engine after the execution. Execution engine is responsible for the specific code calls and execution. For now, all of the Java virtual machine execution engine is the same: the input is byte code file, the process is equivalent byte code parsing process, the output is the result. Runtime Data Area is used to store data and information on the program is running, it is often said of JVM memory.

Runtime Data Area

Here Insert Picture Description
JVM specification memory partitioned by the method area, the heap, the virtual machine stack, program counter, native method stacks composition.

  • 方法区(Mehtod Area):属线程共享内存区域,作用是储存已被JVM加载的类信息、常量、静态变量、即时编译后的代码等。它是堆的一个逻辑部分,为了与堆区分开,又叫Non-Heap,相对而言,GC对于这个区域的收集是很少出现的。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常。

    • 其中的Runtime Constant Pool(运行时常量池), 用于存放编译器生成的符号引用和字面量(就是这个量本身,如字符串“ “ABC” ”,int型"3"),由于Java不要求常量一定在编译时产生,所以它具备 动态性 特征,运行期间产生的新常量也会加入池中。
  • Java堆(Heap):属线程共享内存区域,在虚拟机启动时创建,占用区域最大,用于存放对象实例,所有对象实例和数组都要在堆上分配内存,可以处理不连续的内存空间,可扩展,是GC机制管理的主要区域,所以也被叫做GC堆。当堆中没有内存满足实例分配需求,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常。
    为了支持垃圾收集,堆被分为三个部分:

    • 年轻代 : 常常又被划分为Eden区和Survivor(From Survivor To Survivor)区(Eden空间、From Survivor空间、To Survivor空间(空间分配比例是8:1:1)
    • 老年代
    • 永久代 (jdk 8已移除永久代)
  • 虚拟机栈(JVM Stacks):属线程私有内存区域, 也是常说的栈。Java栈是Java方法执行的内存模型。Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当虚拟机栈中没有内存满足实例分配需求,会抛出StackOverflowError和OutOfMemoryError异常。

  • 程序计数器(Program Counter Register):属线程私有内存区域,占一小块内存区域,用于指示当前执行字节码的行号,通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。在JVM规范中规定,如果线程执行的是非native方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是native方法,则程序计数器中的值是undefined。由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,此内存区域是唯一一个在JVM规范中没有规定任何OutOfMemoryError情况的区域。

  • 本地方法栈(Native Method Stacks):属线程私有内存区域,本地方法栈与虚拟机栈发挥的功能非常类似,只是虚拟机栈为虚拟机执行java方法而服务,而本地方法栈为虚拟机执行native方法而服务。当本地方法栈中没有内存满足实例分配需求,会抛出StackOverflowError和OutOfMemoryError异常。

Java内存模型(JMM)

JMM是一种抽象的概念,它是一种规范,定义了程序中各个变量访问的方式。JVM运行程序的实体是线程,每个线程创建时JVM会为其创建相应的工作内存(空栈间),用于储存线程私有数据,JMM中规定所有变量都存储在主内存上,所有线程都可访问,线程对于变量的操作(赋值、读取等)必须在工作内存进行,操作完成写回主内存,这样各线程之间就无法相互访问,线程间的通信(传值)必须通过主内存来完成。

  • 主内存(堆内存):主要存储实例对象,所有线程创建的实例对象(成员、局部、静态、常量等)都放在主内存中。存在线程安全问题(造成主内存与工作内存间数据存在一致性问题)。
  • 工作区域(私有线程域):主要存储当前方法的所有本地变量信息(主内存中变量的复制,也包含字节码行号指示器、相关Native方法信息)。线程中的本地变量对其他线程不可见,不存在线程安全问题。

主内存与工作内存的数据存储类型、操作方式及与硬件的关系

如果方法中的数据是基本数据类型,将直接存储在栈帧结构中;如果本地变量是引用类型,那么该引用会存储在工作内存的栈帧中,而对象实例还是会存在主内存(堆)中。对于实例对象的成员变量,无论类型都被存在堆中。当两个线程同时调用了一个对象的同一个方法时,两条线程都会将所涉及的数据复制一份到自己的工作内存中,操作完成后刷新到主内存中。JMM是一种抽象的概念,并不实际存在,在逻辑上分工作内存和主内存,但在物理上二者都可能在主存中也可能在Cache或者寄存器中。Java内存分区也是这个道理。

Java线程的实现原理

在Windows和Linux系统上,Java线程实现是基于一对一的线程模型,即通过语言级的程序(JVM)去间接地调用操作系统内核的线程模型。由于我们编写的多线程程序属于语言层面的,程序一般不会直接去调用内核线程,取而代之的是一种轻量级的进程(Light Weight Process),也是通常意义上的线程,由于每个轻量级进程都会映射到一个内核线程,因此我们可以通过轻量级进程调用内核线程,进而由操作系统内核将任务映射到各个CPU各个核心进行并发执行,这种轻量级进程与内核线程间1对1的关系就称为一对一的线程模型。
Here Insert Picture Description
关于其中的内核线程(Kernel-Level Thread,KLT),它是由操作系统内核(Kernel)支持的线程,这种线程是由操作系统内核来完成线程切换,内核通过操作调度器进而对线程执行调度,并将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这也就是操作系统可以同时处理多任务的原因。

并发编程的问题

多线程并发编程会涉及到以下的问题:

  • 原子性:指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。
  • 可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
  • 有序性:程序执行的顺序按照代码的先后顺序执行,多线程中为了提高性能,编译器和处理器的常常会对指令做重排(编译器优化重排、指令并行重排、内存系统重排)。

JMM的具体实现

JMM还提供了一系列原语(由若干条指令组成的,用于完成一定功能的一个过程),封装了底层实现。

  • 原子性:Java提供了两个高级字节码指令monitorenter和monitorexit,对应的是关键字synchronized,使用该关键字保证方法和代码块内的操作的原子性。

  • Visibility: Java in the volatile keyword provides a feature that is variable in its modified after being modified can be synchronized to the main memory immediately, by its modified variables are flushed from main memory before each is used. Therefore, you can use volatile to ensure the visibility of multi-threaded operation variables.
    In addition to volatile, Java and the final two in the synchronized keyword visibility can be achieved, but achieved in different ways.

  • Ordering: prohibition instruction using the volatile keyword rearrangement, lock with the synchronized keyword.

Reprinted: https: //blog.csdn.net/qq_41297896/article/details/89949632

Published 74 original articles · won praise 23 · views 40000 +

Guess you like

Origin blog.csdn.net/qxhly/article/details/104637822