圆月弯刀
深入了解JVM
本文首先介绍一下
Java
虚拟机的生存周期,然后大致介绍
JVM
的体系结构,最后对体系结构中的各个部分进行详细介绍。(
首先这里澄清两个概念:
JVM
实例和
JVM
执行引擎实例,
JVM
实例对应了一个独立运行的
java
程序,而
JVM
执行引擎实例则对应了属于用户运行程序的线程;也就是
JVM
实例是进程级别,而执行引擎是线程级别的。)
一、
JVM
的生命周期
JVM
实例的诞生:当启动一个
Java
程序时,一个
JVM
实例就产生了,任何一个拥有
public static void main(String[] args)
函数的
class
都可以作为
JVM
实例运行的起点,既然如此,那么
JVM
如何知道是运行
class A
的
main
而不是运行
class B
的
main
呢?这就需要显式的告诉
JVM
类名,也就是我们平时运行
java
程序命令的由来,如
java classA hello world,
这里
java
是告诉
os
运行
Sun java 2 SDK
的
java
虚拟机,而
classA
则指出了运行
JVM
所需要的类名。
JVM
实例的运行:
main
()作为该程序初始线程的起点,任何其他线程均由该线程启动。
JVM
内部有两种线程:守护线程和非守护线程,
main
()属于非守护线程,守护线程通常由
JVM
自己使用,
java
程序也可以标明自己创建的线程是守护线程。
JVM
实例的消亡:当程序中的所有非守护线程都终止时,
JVM
才退出;若安全管理器允许,程序也可以使用
Runtime
类或者
System.exit()
来退出。
二、
JVM
的体系结构
粗略分来,
JVM
的内部体系结构分为三部分,分别是:类装载器(
ClassLoader
)子系统,运行时数据区,和执行引擎。
下面将先介绍类装载器,然后是执行引擎,最后是运行时数据区
1
,类装载器,顾名思义,就是用来装载
.class
文件的。
JVM
的两种类装载器包括:启动类装载器和用户自定义类装载器,启动类装载器是
JVM
实现的一部分,用户自定义类装载器则是
Java
程序的一部分,必须是
ClassLoader
类的子类。(下面所述情况是针对
Sun JDK1.2
)
动类装载器:只在系统类
(java API
的类文件
)
的安装路径查找要装入的类
用户自定义类装载器:
系统类装载器:在
JVM
启动时创建,用来在
CLASSPATH
目录下查找要装入的类其他用户自定义类装载器:这里有必要先说一下
ClassLoader
类的几个方法,了解它们对于了解自定义类装载器如何装载
.class
文件至关重要。
protected final Class defineClass(String name, byte data[], int offset, int length)
protected final Class defineClass(String name, byte data[], int offset, int length, ProtectionDomain protectionDomain);
protected final Class findSystemClass(String name)
protected final void resolveClass(Class c)
defineClass
用来将二进制
class
文件(新类型)导入到方法区
,
也就是这里指的类是用户自定义的类(也就是负责装载类)
findSystemClass
通过类型的全限定名,先通过系统类装载器或者启动类装载器来装载,并返回
Class
对象。
ResolveClass:
让类装载器进行连接动作(包括验证,分配内存初始化,将类型中的符号引用解析为直接引用),这里涉及到
java
命名空间的问题,
JVM
保证被一个类装载器装载的类所引用的所有类都被这个类装载器装载,同一个类装载器装载的类之间可以相互访问,但是不同类装载器装载的类看不见对方,从而实现了有效的屏蔽。
2
,
执行引擎:它或者在执行字节码,或者执行本地方法
要说执行引擎,就不得不的指令集,每一条指令包含一个单字节的操作码,后面跟
0
个或者多个操作数。
(一)指令集以栈为设计中心,而非以寄存器为中心这种指令集设计如何满足
Java
体系的要求:
平台无关性:以栈为中心使得在只有很少
register
的机器上实现
java
更便利
compiler
一般采用
stack
向连接优化器传递编译的中间结果,若指令集以
stack
为基础,则有利于运行时进行的优化工作与执行即时编译或者自适应优化的执行引擎结合,通俗的说就是使编译和运行用的数据结构统一,更有利于优化的开展。
网络移动性:
class
文件的紧凑性。
安全性:指令集中绝大部分操作码都指明了操作的类型。(在装载的时候使用数据流分析期进行一次性验证,而非在执行每条指令的时候进行验证,有利于提高执行速度)。
(二)执行技术
主要的执行技术有
:
解释,即时编译,自适应优化、芯片级直接执行其中解释属于第一代
JVM
,即时编译
JIT
属于第二代
JVM
,自适应优化(目前
Sun
的
HotspotJVM
采用这种技术)则吸取第一代
JVM
和第二代
JVM
的经验,采用两者结合的方式
自适应优化:开始对所有的代码都采取解释执行的方式,并监视代码执行情况,然后对那些经常调用的方法启动一个后台线程,将其编译为本地代码,并进行仔细优化。若方法不再频繁使用,则取消编译过的代码,仍对其进行解释执行。
3
,运行时数据区:主要包括:方法区,堆,
java
栈,
PC
寄存器,本地方法栈
(
1
)方法区和堆由所有线程共享
堆:存放所有程序在运行时创建的对象
方法区:当
JVM
的类装载器加载
.class
文件,并进行解析,把解析的类型信息放入方法区。
(
2
)
Java
栈和
PC
寄存器由线程独享,在新线程创建时间里
(
3
)本地方法栈:
存储本地方法调用的状态
上边总体介绍了运行时数据区的主要内容,下边进行详细介绍,要介绍数据区,就不得不说明
JVM
中的数据类型。
JVM
中的数据类型:
JVM
中基本的数据单元是
word,
而
word
的长度由
JVM
具体的实现者来决定
数据类型包括基本类型和引用类型,
(
1
)基本类型包括:数值类型
(
包括除
boolean
外的所有的
java
基本数据类型
)
,
boolean
(在
JVM
中使用
int
来表示,
0
表示
false
,其他
int
值均表示
true
)和
returnAddress
(
JVM
的内部类型,用来实现
finally
子句)。
(
2
)引用类型包括:数组类型,类类型,接口类型
前边讲述了
JVM
中数据的表示,下面让我们输入到
JVM
的数据区
首先来看方法区:
上边已经提到,方法区主要用来存储
JVM
从
class
文件中提取的类型信息,那么类型信息是如何存储的呢?众所周知,
Java
使用的是大端序(
big?endian:
即低字节的数据存储在高位内存上,如对于
1234
,
12
是高位数据,
34
为低位数据,则
java
中的存储格式应该为
12
存在内存的低地址,
34
存在内存的高地址,
x86
中的存储格式与之相反)来存储数据,这实际上是在
class
文件中数据的存储格式,但是当数据倒入到方法区中时,
JVM
可以以任何方式来存储它。
类型信息:包括
class
的全限定名,
class
的直接父类,类类型还是接口类型,类的修饰符(
public,
等),所有直接父接口的列表,
Class
对象提供了访问这些信息的窗口(可通过
Class.forName(
“”
)
或
instance.getClass()
获得),下面是
Class
的方法,相信大家看了会恍然大悟,(原来如此
J
)
getName(), getSuperClass(), isInterface(), getInterfaces(), getClassLoader();
static
变量作为类型信息的一部分保存
指向
ClassLoader
类的引用:在动态连接时装载该类中引用的其他类
指向
Class
类的引用:必然的,上边已述
该类型的常量池:包括直接常量(
String
,
integer
和
float point
常量)以及对其他类型、字段和方法的符号引用(注意:这里的常量池并不是普通意义上的存储常量的地方,这些符号引用可能是我们在编程中所接触到的变量),由于这些符号引用,使得常量池成为
java
程序动态连接中至关重要的部分
字段信息:普通意义上的类型中声明的字段
方法信息:类型中各个方法的信息
编译期常量:指用
final
声明或者用编译时已知的值初始化的类变量
class
将所有的常量复制至其常量池或者其字节码流中。
方法表:一个数组,包括所有它的实例可能调用的实例方法的直接引用(包括从父类中继承来的)
除此之外,若某个类不是抽象和本地的,还要保存方法的字节码,操作数栈和该方法的栈帧,异常表。
举例:
class Lava{
private int speed = 5;
void flow(){}
class Volcano{
public static void main(String[] args){
Lava lava = new Lava();
lava.flow();
}
}
运行命令
java Volcano;
(
1
)
JVM
找到
Volcano.class
倒入,并提取相应的类型信息到方法区。通过执行方法区中的字节码,
JVM
执行
main
()方法,(执行时会一直保存指向
Vocano
类的常量池的指针)
(
2
)
Main
()中第一条指令告诉
JVM
需为列在常量池第一项的类分配内存(此处再次说明了常量池并非只存储常量信息),然后
JVM
找到常量池的第一项,发现是对
Lava
类的符号引用,则检查方法区,看
Lava
类是否装载,结果是还未装载,则查找“
Lava.class
”
,
将类型信息写入方法区,并将方法区
Lava
类信息的指针来替换
Volcano
原常量池中的符号引用,即用直接引用来替换符号引用。
(
3
)
JVM
看到
new
关键字,准备为
Lava
分配内存,根据
Volcano
的常量池的第一项找到
Lava
在方法区的位置,并分析需要多少对空间,确定后,在堆上分配空间,并将
speed
变量初始为
0
,并将
lava
对象的引用压到栈中
(
4
)调用
lava
的
flow
()方法
好了,大致了解了方法区的内容后,让我们来看看堆
java
对象的堆实现:
java
对象主要由实例变量(包括自己所属的类和其父类声明的)以及指向方法区中类数据的指针,指向方法表的指针,对象锁(非必需),
等待集合(非必需),
GC
相关的数据(非必需)(主要视
GC
算法而定,如对于标记并清除算法,需要标记对象是否被引用,以及是否已调用
finalize
()方法)。
那么为什么
java
对象中要有指向类数据的指针呢?我们从几个方面来考虑
首先:当程序中将一个对象引用转为另一个类型时,如何检查转换是否允许?需用到类数据
其次:动态绑定时,并不是需要引用类型,而是需要运行时类型,
这里的迷惑是:为什么类数据中保存的是实际类型,而非引用类型?这个问题先留下来,我想在后续的读书笔记中应该能明白
指向方法表的指针:这里和
C++
的
VTBL
是类似的,有利于提高方法调用的效率
对象锁:用来实现多个线程对共享数据的互斥访问
等待集合:用来让多个线程为完成共同目标而协调功过。(注意
Object
类中的
wait(),notify(),notifyAll()
方法)。
Java
数组的堆实现:数组也拥有一个和他们的类相关联的
Class
实例,具有相同
dimension
和
type
的数组是同一个类的实例。数组类名的表示:如
[[Ljava/lang/Object
表示
Object[][]
,
[I
表示
int[]
,
[[[B
表示
byte[][][]
至此,堆已大致介绍完毕,下面来介绍程序计数器和
java
栈
程序计数器:为每个线程独有,在线程启动时创建,
若
thread
执行
java
方法,则
PC
保存下一条执行指令的地址。
若
thread
执行
native
方法,则
Pc
的值为
undefined
Java
栈:
java
栈以帧为单位保存线程的运行状态,
java
栈只有两种操作,帧的压栈和出栈。
每个帧代表一个方法,
java
方法有两种返回方式,
return
和抛出异常,两种方式都会导致该方法对应的帧出栈和释放内存。
帧的组成:局部变量区(包括方法参数和局部变量,对于
instance
方法,还要首先保存
this
类型,其中方法参数按照声明顺序严格放置,局部变量可以任意放置),操作数栈,帧数据区(用来帮助支持常量池的解析,正常方法返回和异常处理)。
本地方法栈:依赖于本地方法的实现,如某个
JVM
实现的本地方法借口使用
C
连接模型,则本地方法栈就是
C
栈,可以说某线程在调用本地方法时,就进入了一个不受
JVM
限制的领域,也就是
JVM
可以利用本地方法来动态扩展本身。
posted on 2006-10-10 13:57
圆月弯刀
阅读(699)
评论(0)
编辑
收藏
新用户注册
刷新评论列表
只有注册用户
登录
后才能发表评论。
网站导航:
博客园
IT新闻
知识库
C++博客
博问
管理
Powered by:
BlogJava
Copyright © 圆月弯刀
<
2006年10月
>
日
一
二
三
四
五
六
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
导航
BlogJava
首页
新随笔
联系
聚合
管理
统计
随笔 - 7
文章 - 0
评论 - 3
引用 - 0
常用链接
我的随笔
我的评论
我的参与
最新评论
留言簿
(1)
给我留言
查看公开留言
查看私人留言
随笔档案
2006年10月 (6)
2006年9月 (1)
最新随笔
1. Spring的任务调度服务实例讲解 {转}
2. Java中的几个小问题
3. 深入了解JVM
4. JVM
5. JVM(Java虚拟机)启动{转}
6. 关于ClassLoader [转]
7. 我心爱的婷婷,我不会让你再哭
搜索
积分与排名
积分 - 10235
排名 - 2349
最新评论
1. re: Java中的几个小问题
如果你是编程高手,这里将是你一个展现自我的新舞台----www.mylinux.com.cn
--1
2. re: 我心爱的婷婷,我不会让你再哭
唉~~ 谈恋爱真麻烦................还是计算机听话
--小强
3. re: 我心爱的婷婷,我不会让你再哭
和我当时差不多,同情
--坏男孩
阅读排行榜
1. JVM(4088)
2. JVM(Java虚拟机)启动{转}(2436)
3. Java中的几个小问题(1269)
4. 关于ClassLoader [转] (813)
5. 深入了解JVM(699)
评论排行榜
1. 我心爱的婷婷,我不会让你再哭(2)
2. Java中的几个小问题(1)
3. 深入了解JVM(0)
4. JVM(0)
5. JVM(Java虚拟机)启动{转}(0)