《深入理解java虚拟机》 - 需要一本书来融汇贯通你的经验(上)
工作中,也许我们能够碰到各种各样的问题,也许碰不到。这些经历确实会让我们成长很多,但是,及时遇到的问题再多,我觉得,那些应是零散的,很难形成体系结构的。
所以,我们很多时候是需要书来建立一些完整的体系的。
前段时间,买了本《深入理解java虚拟机》,花了一周多的时间看了一遍,然后再简单回顾了下,觉得有必要写点读后感。因为,我们只有七秒钟的记忆,记下来后面翻阅。尽管书上也有,但毕竟不是自己的。转化是需要时间的。
如下正题!
首先一点,现在的技术呢,更新换代很快,所以,一定要尽可能的接受最新的资料,所以,要买肯定买最新版了。里面介绍的结合了jdk1.7,还算可以吧。
翻开目录,大概就知道要读些啥了。
第一部分 走进java
我觉得,都几十岁的人了,就不要浪费大家的时间了吧。直接跳过,不会去读了。
第二部分 自动内存管理
这部分就比较重要了,也许你说讲的 堆栈内存你都知道,但是,你真的透彻吗?还是仔细看看的好。如下这个图,我觉得贴得不多于。
1. 程序计数器。 当前执行的字节码的行号指示器。和汇编中的 CS IP 相似,通过改变这个值,从而使代码一步步执行下去。
2. 虚拟机栈。每个方法执行时,都会创建一个栈帧(Current Stack Frame),用于存储局部变量表(Local Stack Frame)、操作数栈(Operand Stack)、动态链接(Dynamic Linking)、返回地址(Return Address)等,每个方法从调用执行到完成,就对应着一栈帧在虚拟机中的入栈和出栈过程。
3. 本地方法栈。与虚拟机栈功能类似,不过是作用于native方法。
4. java堆。java虚拟机中管理的最大的一块内存。所有线程共享该内存,他的作用就是用来存放对象实例,几乎所有的对象实例都在这里分配。因是几乎所有对象的存放区域,所以也是gc的主要区域了。
5. 方法区。他用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,他是所有线程共享的。
6. 直接内存。这是jdk1.4后,使用native函数直接分配的directbytebuffer。此处的内存不java堆大小的限制。
7. 内存的分配方式有两种,指针碰撞和空闲列表,采用的分配方式取决于java堆是否规整,而堆是否规整又取决于垃圾回收器是否具有压缩功能。
虽然都是概念性的东西,但是,当你把个细节都了解后,是需要这么一个总体印象的。
第3单 垃圾收集器与内在分配策略
哪些内存需要回收? 什么时候回收? 如何回收?
回答完这几个问题,本章就算完成了。
1. 垃圾回收算法。
引用计数器算法。算法简单,只要计数器值为0就可以进行回收,但是存在相互循环引用时无法进行回收的问题。
可达性分析算法(gc roots)。目前主流的收集方法。
标记-清除算法、复制算法、标记整理算法、分代收集算法。
收集器:serial 收集器、parnew收集器、parallel scavenge收集器、serial old收集器、cms 收集器、G1收集器(据说是最前沿的收集器)
GC日志的查看。
第4章 虚拟机性能监控与故障处理工具
jps 查看虚拟机进程状况。
jstat (jvm statistics monitoing tool) 用于监视虚拟机各种运行状态信息,可以显示本地或远程虚拟机中的类装载、内存、垃圾收集、jit编译等运行参数。
jinfo 查看配置,jhat 快照查看工具,此二命令意义不大。
jmap 用于生成堆转储快照。
jstack 用于生成线程快照。
gui工具,jconsole, visual vm(插件可用),btrace。
第三部分 虚拟机执行子系统
第6章 类文件结构
字节码是虚拟机的,不是java的,虚拟机可以执行其他语言的文件,符合虚拟机规范即可。
class 文件的头4个字节称为魔数,值为:0xcafebabe; 第5-6位为次版本号,7-8位为主版本号。根据该版本号来确定,运行的java版本是否是虚拟机支持的版本,向后兼容。
接下来是常量池,它是其他项目关联最多的数据类型,也是占用class空间最大的项目之一,同时还是第一个出现的表类型数据项目。
类型 | 标志 | 描述 |
CONSTANT_utf8_info | 1 | UTF-8编码的字符串 |
CONSTANT_Integer_info | 3 | 整形字面量 |
CONSTANT_Float_info | 4 | 浮点型字面量 |
CONSTANT_Long_info | 5 | 长整型字面量 |
CONSTANT_Double_info | 6 | 双精度浮点型字面量 |
CONSTANT_Class_info | 7 | 类或接口的符号引用 |
CONSTANT_String_info | 8 | 字符串类型字面量 |
CONSTANT_Fieldref_info | 9 | 字段的符号引用 |
CONSTANT_Methodref_info | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 字段或方法的符号引用 |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_MothodType_info | 16 | 标志方法类型 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
访问标志,和类里写的方法权限是一致的。
标志名
|
标志值
|
标志含义
|
针对的对像
|
ACC_PUBLIC
|
0x0001
|
public类型
|
所有类型
|
ACC_FINAL
|
0x0010
|
final类型
|
类
|
ACC_SUPER
|
0x0020
|
使用新的invokespecial语义
|
类和接口
|
ACC_INTERFACE
|
0x0200
|
接口类型
|
接口
|
ACC_ABSTRACT
|
0x0400
|
抽象类型
|
类和接口
|
ACC_SYNTHETIC
|
0x1000
|
该类不由用户代码生成
|
所有类型
|
ACC_ANNOTATION
|
0x2000
|
注解类型
|
注解
|
ACC_ENUM
|
0x4000
|
枚举类型
|
枚举
|
描述符标识字符含义
标识字符 含义
B byte
C char
D double
F float
I int
J long
S short
Z boolean
V void
L Object, 例如 Ljava/lang/Object
一维数组用[ 表示,二维数组用 [[ 表示,以此类推。表示方法如,void inc(); -> ()V; int indexof(char s[], int offset) -> ([CI)I;
方法表集合,属性表集合。
code属性不是必须的,但是是在绝大多数情况下,占用最多也是最重要的一个属性。它可以理解class文件的具体数据内容。 javap -verbose testclass 输出class文件的字节码。
exceptions属性,用于异常处理。
linenumbertable属性,用于描述java源码行号与字节码行号,便于排错。
localveriabletable,用于描述栈帧中局部变量表中的变量与java源码中定义的变量之间的关系,也用于排错。
sourcefile属性,记录生成这个class文件的源码文件名称。
constantvalue,为静态变量赋值。
stackmaptable,在类加载的字节码验证阶段,被新类型检查验证器使用,用于替代类型推到器。
signature,比如,泛型被擦除后,该标识保留了泛型数据下来。
bootstrapmethods属性。
字节码指令,这个就没多大作用了,稍微理解下就好。
第7章 虚拟机类加载机制
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
类加载器。通过一个类的全局限定名来获取此类的二进制字节流让用户自己实现,以便应用程序自己决定如何去获取所需要的类。
双亲委派模型。简单的说就是,优先请求父类加载器,再用子加载器加载。
其实东西很多,要用千把个字描写完内容,还是太唐突了。
下一章,未完待续。。。