抽时间从新读了一遍《深刻理解JVM》一书。如下为摘录内容。java
java虚拟机运行时数据区程序员
是一块较小的内存空间,能够看作是当前线程所执行的字节码的行号指示器。每条线程都有一个独立的程序计数器,各条线程之间计数器互不影响。算法
描述的是java方法执行的内存模型:每一个方法在执行的同时都会建立一个栈帧用于存储局部变量表、操做数栈、动态连接、方法出口等信息。安全
局部变量表存放了编译器可知的各类基本数据类型、对象引用和returnAddress类型。数据结构
虚拟机栈为虚拟机执行java方法服务,二本地方法栈为虚拟机使用到的Native方法服务。多线程
被全部线程共享的一块内存区域,在虚拟机启动时建立。java堆是垃圾收集器管理的主要区域,所以不少时候也被叫作GC堆。并发
各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。函数
方法区的一部分。须要注意的是string的intern方法在jdk1.6先后的不一样。jdk1.6以后常量池放到了堆中。spa
并非虚拟机运行时数据区的一部分,也不是java虚拟机规范中国定义的内存区域。NIO引入的通道和缓冲区可使用native函数库直接分配对外内存。线程
引用计数算法:很难解决对象之间相互循环引用的问题
可达性分析算法:经过一系列GC Roots的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路线称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证实此对象是不可用的。
标记-清除算法:效率低,空间碎片化
复制算法:运行简单高效,代价高,下降了一半的使用率
标记-整理算法
分代收集:新生代用复制算法,老年代用标记整理算法
加载、验证、准备、解析、初始化。
1)遇到new、getstatic、putstatic或invokestatic这4条指令字节码时,若是类没有进行过初始化,则须要先触发其初始化。
2)使用java.lang.reflect包的方法对类进行反射调用的时候,若是类没有进行过初始化,则须要先触发其初始化。
3)当初始化一个类的时候,若是发现其父类尚未进行过初始化,须要先触发其父类的初始化。
4)当虚拟机启动时,须要制定main,虚拟机会先初始化main类。
5)当使用jdk1.7的动态语言支持时,若是java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,而且这个方法句柄所对应的类没有进行过初始化,则须要先触发其初始化。
1)经过一个类的全限定名来获取定义此类的二进制字节流
2)将这个字节流所表明的静态存储结构转换为方法区的运行时数据结构
3)在内存中生成一个表明这个类的java.lang.Class对象,做为方法区这个类的各类数据的访问入口。
确保Class文件的字节流中包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。
文件格式验证——元数据验证——字节码验证——符号引用验证
正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(被static修饰的变量),二不包括实例变量。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
类和接口的解析、字段解析、类方法解析、接口方法解析
类初始化阶段是类加载过程的最后一步。在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员经过程序指定的主观计划去初始化类变量和其余资源,或者能够从另外一个角度来表达:初始化阶段是执行类构造器<clinit>()方法的过程。
硬件的效率与一致性
java内存模型(JMM)
线程、主内存、工做内存之间的交互关系
java内存模型规定了全部的变量都存储在主内存中,每条线程有本身的工做内存,线程的工做内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的全部操做(读取、赋值)都必须在工做内存中进行,而不能直接读写主内存中的变量。线程间变量值的传递均须要经过主内存来完成。
java内存模型时围绕着在并发过程当中如何处理原子性、可见性和有序性这三个特征来创建的。
先行发生原则 保证了咱们大多数状况下不用关心太多。