JVM概述
个人理解:JVM是一种规范,Java内存区域是遵循规范对物理内存的一种虚拟化。通过虚拟化技术,将物理内存分为不同的区域,便于JVM对进行内存操作。
Java内存区域
Java虚拟机在程序运行过程中,把内存分为几个区域,不同区域有不同的用途。根据Java虚拟机规范,Java虚拟机管理的内存包括以下几个运行时数据区:
程序计数器
在内存中分配很小的区域。当前线程执行字节码的行号指示器,字节码解释器根据程序计数器获取下一条执行的指令。调用native方法时,程序计算器的值是undefine。 由于多CPU之间线程切换,所以每个线程都有自己的程序计数器,是线程私有的,内存很小,也是唯一不会发生OOM的区域。
虚拟机栈
线程私有,存储线程的调用栈。在一个线程中,Java程序执行都是方法调方法,每个方法被调用的时候生成一个栈帧压入虚拟机栈,执行完之后,栈帧从栈顶弹出。一个方法的执行过程对应栈帧的入栈和出栈过程。 栈帧包括局部变量表、操作数栈、动态链接、方法出口。 局部变量表:存放编译器的基本数据类型、对象引用。 操作数栈:存放程序执行过程,数据的读写栈。 StackOverflowError:栈的深度大于虚拟机规定的深度。 OutOfMemoryError:但栈扩展无法申请到足够的内存。 -Xss设置栈大小
本地方法栈
与虚拟机栈基本一致,存放的是native方法的栈帧。
堆
Java对是线程共享的区域,存放几乎全部的对象(通过逃逸分析,如果无逃逸可能的对象会在栈上分配) 堆无法扩展是抛出OOM。 -Xmx 最大堆大小 -Xms 初始堆大小
方法区
线程共享,用于存储被虚拟机加载的类型信息、常量、静态变量、即时编译器编译的代码缓存。 -XX:MaxPermSize
运行时常量池
方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
直接内存
直接内存不是运行时数据区的部分,也不是Java虚拟机规范,NIO类引入基于通道和缓存区的IO方式,可以使用native函数库直接操作堆外内存,通过Java堆里的DirectByteBuffer对象作为引用直接使用,避免在Java堆和Native堆中复制数据提升性能。
Java内存模型
Java内存模型屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。Java内存模型的主要目的是定义程序中各种变量(线程共享)的访问规则,即关注在虚拟机中把变量值存储到内存和从内存中取出变量值这样的底层细节。 主内存:线程共享的内存区域,方法区、堆区。 工作内存:线程私有的内存区域,栈区。 个人理解:内存模型定义了并发时主内存和工作内存数据交换的规则,以保障并发时数据一致性的问题。
内存间的交互操作
- lock:作用于主内存的变量,把一个变量标识为一条线程独占的状态。
- unlock:作用于主内存的变量,把一个处于锁定状态的变量释放出来,可以被其他线程锁定。
- read:作用于主内存的变量,将数据读到工作内存,以便load使用。
- load:作用于工作内存的变量,将read读取的数据放入工作内存的副本变量。
- use:作用于工作内存的变量,将工作内存的变量传递给执行引擎
- assign:作用于工作内存的变量,把一个从执行引擎接收到的数据载入工作内存中。
- store:作用于工作内存的变量,把一个工作内存的数据传递到主内存,以便write使用。
- write:作用于主内存的变量,把store操作的数据载入到主内存中。
happen-before原则
- 程序次序规则:控制流顺序,说白了就代码顺序执行。
- 管程锁定规则:一个unlock操作要先行发生于后面对同一个锁的lock操作。也就是同一把锁只能同时被lock一次。
- volatile变量规则:对volatile变量的写操作先行发生于后面对这个变量的读操作。内存屏障保证。
- 线程启动规则:Tread对象的start()方法先行发生于此线程的每一个动作。
- 线程终止规则:线程的所有操作都先行发生于对此线程的终止检测。
- 线程中断规则:对线程的interrupt()方法调用先行发生于被中断线程的代码检测到中断事件的发生。interrupted()方法检测到是否有中断发生。
- 对象终结规则:一个对象的初始化完成先行发生于finalize()方法的开始。
- 传递性:如果操作A先行发生于操作B,操作B先行发生于操作C,那么操作A先行发生于操作C。
原子性、可见性、有序性
原子性:原子性变量操作包括read、load、assign、use、store、write,基本数据类型的访问和读写可以认为是具备原子性的。synchronized同步代码块之间的操作保证原子性。 可见性:一个线程修改了共享变量的值时,其他线程能够立即得知这个修改。 volatile保证可见性是通过Java内存模型在写操作后插入一个StoreLoad屏障,在读操作前插入一个LoadLoad屏障。 同步块保证可见性的依据是“对一个变量执行unlock操作之前,必须把变量同步到主内存(store、write)” final关键字的可见性是“一旦构造器初始化完成,并且构造器没有把this传递出去,那么在其他线程就能看到final字段的值”。 有序性:如果在本线程内观察,所有操作都是有序的;如果在一个线程观察另一个线程,所有操作都是无序的。前半句说的是程序次序规则,后半句指的是“指令重排序”和“工作内存与主内存同步延迟”。volatile(禁止指令重排序)+synchronized(对变量lock操作)保证线程间的有序性。
|