深入理解JAVA虚拟机--读书笔记(一)

2014-11-23 22:28:18 · 作者: · 浏览: 0

深入理解JAVA虚拟机 ----JVM高级特性与最佳实践

作者:周志明

读后感:这本书不用我多说,很多人推荐,自己读完后也同样感觉很好,做java相关的朋友强烈推荐。以下是我的读书笔记,记下了一些我关注的要点,多数是摘要,有的是自己组织的语言,如有不妥,请指正。

1.jvm 内存结构

1)程序计数器
较小的内存区域,可以看作是当前线程所执行的字节码的行号指示器;每条线程独立;
2)java虚拟机栈
描述java方法执行的内存模型,即每个方法在执行的同时都会创建一个栈帧用户存储局部变量表、操作数栈、动态链接、方法出口等信息;即一个方法的调用直至完成的过程对应着一个栈帧的入栈到出栈的过程。同样是线程私有;
3)本地方法栈
对应虚拟机使用的native方法,类似java虚拟机栈;
4)java 堆
虚拟机启动时创建,几乎所有对象实例以及数据都要在堆上分配(除jit,逃逸分析等优化技术外),大小可扩展,通过-Xmx和-Xms参数控制;线程共享;
5)方法区
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;
6)运行时常量池
可看作方法区的一部分,存放编译期(并不一定只有编译期)生成的各种字面量和符号引用
7)直接内存
可看做是堆外内存,不受java堆大小限制;

以上分类我自己这样分类,便于理解:栈(虚拟机栈和本地房发展),堆,方法区(永久代?),程序计数器,直接内存;

2.gc回收

1)收集算法:
引用技术算法:引用加1,引用失效减1;很难解决对象之间循环引用的;
可达性分析算法(java 主流实现):当一个对象到GC Roots没有任何引用链相连时,证明此对象不可用;
标记-清除算法:所有算法的基础,分为“标记”和“清除”两个阶段;效率低,产生大量不连续内存碎片;
复制算法:将内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块用完后,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。实现简单,运行高效,浪费空间;
标记整理算法:标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存;
分代收集算法:(当前商业虚拟机的垃圾收集都采用),一般分为新生代和老年代,新生代只有少量存活对象,使用“复制算法”收集,老年代,存活率高,使用“标记-清理”或者“标记-整理”算法来回收。

回收方法区:或称永久代,性价比较低,反射,动态代理,动态生成jsp等要关注此区域;

2)内存分配
大方向讲就是在堆上分配(但也可能经过JIT编译后被拆散为标量类型并间接地栈上分配),对象主要分配在新生代的Eden区上,少数情况可能直接分配在老年代中,分配规则不一定是百分之百,取决于哪种垃圾回收器和虚拟机相关参数。

3.类加载机制

1)加载时机,生命周期:加载-->验证-->准备-->解析(此步骤循序不一致)-->初始化-->使用-->卸载
初始化的场景:遇到new,getstatic,putstatic,invokestatic4条字节码;使用java.lang.reflect包进行反射调用;初始化一个类先要初始化父类;执行main方法的类;jdk1.7动态语言时,句柄是REF_getStatic,REF_putStatic,REF_invokeStaticde;
2)类加载过程(以下的定义是截取摘要)
加载:获取类的二进制字节流,将静态存储结构转化为方法区的运行时数据结构,内存中生成一个代表这类的java.lang.Class对象;
验证:保证虚拟机安全,文件格式验证、元数据验证、字节码验证、符号引用验证
准备:正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中分配。
解析:虚拟机将常量池内的符号引用替换为直接引用的过程,
初始化:执行类构造器 ()方法的过程(改方法为线程安全)。包含静态变量、构造行数
3)类加载器
知识点:判断两个类相等(包括equals,isAssignableFrom(),isInstance(),instatceof判断所属关系),只有这两个类是由同一个类加载器加载的前提才有意义。
双亲委派模型:启动类加载器<--扩展类加载器<--应用程序类加载器<--自定义类加载器
工作过程:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都会应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(他的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
见插图
破坏双亲委派模型:1)线程上下文加载器(Theard Context ClassLoader),java.lang.Thread类的setContextClassLoader();2)热加载,OSGi

4.类编译

前段编译把*.java文件转变成*.class文件的过程;后端编译把字节码转变成机器码的过程(JIT);或静态提前编译器直接把*.java文件编译成本地机器码的过程。
语法糖:泛型与类型擦除、自动装箱、拆箱与遍历循环、条件编译;

(晚期)HotSpot内置2中及时编译期:Client Compiler 和Server Compiler (client模式和server模式)
这里主要编译热点代码,热点探测主要有2中方法:基于采样的热点探测(栈顶方法)和基于计数器的热点探测;
编译器的优化技术有:
1)语言无关的经典优化技术之一:公共子表达式消除;
2)语言相关的经典优化技术之一:数组范围检查消除;
3)最重要的优化技术之一:方法内联;(简单理解把目标方法的代码”复制“到发起调用的方法之中,便面发生真是的调用;很复杂的过程啊:))
4)最前沿的优化技术之一:逃逸分析;(JDK1.6)
方法逃逸:当一个对象在方法中定义后,他可能被外部方法所引用,如作为调用参数传递到其他方法中;
线程逃逸:被外部线程访问到,如复制给类变量或者可以在其他线程中访问的实例变量;
如果一个对象没有逃逸,可对其进行高度优化;如:栈上分配;同步消除;标量替换(对象拆解);

5.并发

5.1 java内存模型
主内存与工作内存:java内存模型规定所有变量都存储在主内存中,每条线程还有自己的工作线程,线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。

5.2内存间交互操作:java内存模型定义了8种原子操作,jvm要报保证每一个操作为原子操作,
lock,(锁定,作用于主内存的变量)
unlock,(解锁,作用于主内存的变量)
read(读取,作用于主内存),
load(载入,放入到工作内存中),
use(作用于工作内存的变量),
assign(赋值,作用于工作内存),
store(存储,作用于工作内存的变量);
write(写入,作用于主内存的变量)
【知识点】如果要把一个变量从主内容复制到工作内存,那就要顺序地执行read和load操作,如果把变量从工作内存同步回主内存,就要顺序地执行store和write操作。注意java内存模型只要求