Open chengfengjie opened 6 years ago
现代计算机硬件架构简单图示:
在java中,所有的实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享。局部变量(Local variables),方法定义参数(java 语言规范称之为formal method parameters)和异常处理参数(exception hanlder parameters)不会再线程之间共享,他们不会有内存可见性问题,也不会受内存模型的影响。
JVM运行时内存划分
程序计数器: 程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为当前线程的行号指示器,字节码解释器在工作时,会通过改变这个计数器的值来获取小一条语句指令,每个程序计数器只用来记录一个线程的行号,所以他是线程私有的。如果程序执行的是一个java方法,则计数器记录的是正在执行的虚拟机字节码的指令地址;如果执行的是一个本地(native)方法,则计数器的值为Undefined,由于程序计数器只是记录指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义OutMemeoryError的区域
虚拟机栈(JVM Stack): 一个线程的每个方法在执行的同时,都会创建一个栈帧(Stack Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧JVM栈中入栈,当方法执行完时,栈帧出栈。
StackOverFlowError
;不过多数Java虚拟机都允许动态扩展虚拟机栈的大小,所以线程可以一直申请栈,知道内存不足,此时会抛出OutOfMemoryError
本地方法栈: 本地方法栈在作用、运行机制、异常类型等方面都与虚拟机相同,唯一的区别是:虚拟机栈是执行java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中,会将本地方法栈与虚拟机栈放在一起使用,本地方法栈也是线程私有的。
堆区(Heap): 堆区是理解Java GC机制最重要的区域,没有之一。在JVM所管理的内存中,堆区是最大的一块,堆区也是Java GC机制所管理的主要区域,堆区有所有线程共享,在虚拟机启动时创建,堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存。
GC机制的基本算法是:分代收集。
对象存活的判定:
Java采用可达性分析来判定对象时是否存活。这个算法的基本思路就是通过一系列的称为GC根节点(GC Roots)的对象作为起始点,从这些节点开始进行向下搜索,搜索所走过的路劲称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象时不可用的
Java内存模型内部原理
Java内存模型把Java虚拟机内部划分为线程栈和堆。如图
每一个运行在Java虚拟机里的线程都拥有自己的线程栈。这个线程栈包含了这个线程调用的方法当前执行点相关信息。一个线程仅能访问自己的线程栈,一个线程创建的本地变量对其他线程不可见,仅自己可见。即使两个线程执行同样的代码,这两个线程仍然用自己的线程栈中的代码来创建本地变量,因此,每个线程拥有每个本地变量的独有版本。
所有原始类型的本地变量都存放在线程栈上,因此对其他线程不可见,一个线程可能向另一个线程传递一个原始类型的变量的拷贝,但是它不能共享这个原始类型的变量自身。
堆上包含在Java程序中创建的所有对象,无论是哪一个对象创建的,这包括原始类型的对象版本。如果一个对象被创建后赋值给一个局部变量,或者用来作为另一个对象的成员变量,这个对象仍然是存放在堆上的。
存放在堆上的对象可以被所有持有对这个对象引用的线程访问。当一个线程可以访问一个对象时,他也可以访问这个对象的成员变量,如果两个线程同时调用同一个对象上的同一个方法,他们将会都访问这个对象的成员变量,但是每一个线程都拥有这个本地变量的私有拷贝。