JVM-漫游
Write once, Run Any where. Java Virtual Machine – JVM 的存在让 Java 开发变得简单,并且一次编写多处运行。其实,JVM 就是一个抽象的计算机,它有自己的指令集,有自己的机器语言,有自己的内存管理,只要能生成规范的字节码文件,它都能运行。本文将站在 Java程序员 的角度,带着一个简单的 Java 代码来一次漫游历程,了解一下这些 Java代码 背后的故事。
本文基于 Java HotSpot™ 虚拟机,JDK 8,从整体上进行描述。为了方便描述,将会忽略部分细节。更多详细信息请查看以下内容,传送门:
1. 概述
首先,编写一个 Hello World!
public class Hello {
public static void main(String[] args) {
Hello hello = new Hello();
System.out.println("Hello World! " + hello);
}
}
00000000 ca fe ba be 00 00 00 34 00 1e 0a 00 06 00 10 09
00000010 00 11 00 12 08 00 13 0a 00 14 00 15 07 00 16 07
00000020 00 17 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
00000030 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
00000040 75 6d 62 65 72 54 61 62 6c 65 01 00 04 6d 61 69
00000050 6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67
00000060 2f 53 74 72 69 6e 67 3b 29 56 01 00 0d 53 74 61
00000070 63 6b 4d 61 70 54 61 62 6c 65 01 00 0a 53 6f 75
00000080 72 63 65 46 69 6c 65 01 00 0a 48 65 6c 6c 6f 2e
...
使用 javac 将源代码编译成 JVM 能识别的字节码 - Class 文件(如上二进制),Class 文件描述了一个类或接口的字段,方法,父类,常量池等信息,当我们使用 java 命令去执行一个 class文件或者jar 文件时,就启动了一个 JVM 进程。下面是 java 常用的参数和用法:
java [-options] class [args...] java [-options] -jar jarfile [args...] options: -classpath, -cp : 指定类文件搜索路径 -D<name>=<value> : 设置一个系统属性 -jar : 执行jar包中的应用程序 -verbose, -verbose:class : 显示类加载信息 -verbose:gc : 显示每个垃圾回收事件 -verbose:jni : 显示调用本地方法或接口信息
2. VM Lifecycle
(1)使用 Java 启动 JVM,解析命令行选项,如 -client 或者 -server,加载相应的 VM 库
(2)确定堆大小,编译器类型(client or server);确定环境变量(CLASSPATH)
(3)获取 Main-Class 的类名
(4)新建主线程,调用 JNI_CreateJavaVM 创建 VM,并初始化
(5)从 Main-Class 中获取 main 方法,使用 CallStaticVoidMethod 调用main方法
(6)方法结束,检查并清除可能发生的异常,回调 ExceptionOccurred
(7)主线程使用 DetachCurrentThread 退出,减小线程计数,调用 DestroyJavaVM 退出 JVM
以上就是 JVM 的生命周期,下面着重看一下 JVM的创建,初始化和 JVM的退出。
2.1 JNI_CreateJavaVM
(1)确保只有一个线程调用此方法
(2)检查 JNI 版本,初始化GC日志记录,初始化 OS 模块(如随机数生成器,当前pid,内存页大小,保护页等)
(3)解析传递的命令行参数,初始化标准系统属性
(4)根据解析的参数和属性,进一步初始化 OS模块,堆栈,内存和 safepoint 页。加载VM库如 libzip,libjava。初始化线程库
(5)初始化输出流记录器,初始化一些特定的线程
(6)初始化全局数据,如事件日志,内存分配器,同步原语
(7)创建线程,并把main主线程映射到当前操作系统线程,此时线程还没有添加到线程队列。初始化Java级别的同步
(8)初始化其他模块,如BootClassLoader,Interpreter,Compiler,JNI,SystemDictionary(系统字典)
(9)添加主线程到线程列表,创建VMThread(JVM内部线程,执行一些关键功能)
(10)加载并初始化 Java 核心类库
(11)启动其他辅助线程,并把 JNIEnv 返回给调用者,等待相应其他 JNI 调用
2.2 DestroyJavaVM
(1)等待所有的非守护线程结束
(2)调用 java.lang.Shutdown.shutdown(),若finalization-on-exit为true,调用对象的finalizer
(3)调用 before_exit(),关闭VM级别钩子,停止垃圾收集线程,性能分析线程等
(4)调用 JavaThread::exit(),释放 JNI处理块,移除保护页,移除当前线程
(5)停止 VM线程,停止追踪JNI,将本地运行的线程标记为vm exited,删除当前线程
(6)释放资源,返回调用者
3. Hello World
接下来让我们深入 JVM 内部探究一下这个Hello类,使用到的工具有:jdb(一个命令行调试器,eclipse太大,还占内存OoO)和 HSDB。
首先启动 jdb,并把断点设置到main函数,这里指定使用 SerialGC 和 10m 的堆。
F:\jvm>jdb -XX:+UseSerialGC -Xms10m
正在初始化jdb...
> stop in Hello.main
正在延迟断点Hello.main。
将在加载类后设置。
> run Hello
运行 Hello
设置未捕获的java.lang.Throwable
设置延迟的未捕获的java.lang.Throwable
>
VM 已启动: 设置延迟的断点Hello.main
断点命中: "线程=main", Hello.main(), 行=9 bci=0
9 Hello hello = new Hello();
main[1] next
>
已完成的步骤: "线程=main", Hello.main(), 行=10 bci=8
10 System.out.println("Hello World! " + hello);
main[1] locals
方法参数:
args = instance of java.lang.String[0] (id=405)
本地变量:
hello = instance of Hello(id=406)
main[1]
使用 jps 查询进程id,并用 HSDB(或CLHSDB) 连接到此进程。此时 new 了一个Hello对象,可以知道,它首先在 eden区分配,那么让我们验证一下。

首先,查看一下堆各个代的地址空间(可以看到在JDK 8 中,已经没有PermGen区域了),然后在 eden 区扫描一下看看是否有 Hello 的实例,结果显示有一个,查看这个地址的内容,表示这个对象在main 线程的 TLABs 中,接下来又使用 inspect 命令查看了这个对象的具体内容。使用图形界面看一下这个对象的内容,具体信息如下:

字段的意义如下:
- _mark:Mark Word
- _klass:元数据指针
- _java_mirror:instanceKlass 镜像类,在Java级别访问元信息,就是 java.lang.Class 类,应该是为了安全,包装了一层
- _super:父类
- _layout_helper:正数表示此实例大小,负数表示数组
- _access_flags:访问控制
- _subklass:子类
- _next_sibling下一个兄弟结点
- _array_klasses:数组类型信息
- _nonstatic_field_size:非静态字段域偏移量
- _static_field_size:静态字段域偏移量
- _static_oop_field_count:静态oop字段
- _nonstatic_oop_map_size:实例oop域
- _is_marked_dependent:是否依赖(?)
- _init_state:类状态
- _vtable:内嵌虚方法表
- _itable:内嵌接口方法表
最后在看一下线程内存信息(地址是向上增长的):

- expression stack:这个是操作数栈
- frame:这部分表示栈帧
- locals area:表示局部变量区域
- New Hello:第 1 个局部变量,hello
- NewGen objArray:第 0 个局部变量,字符串数组参数
这里只是 JVM 里的一小部分信息,由于这个 Hello 比较简单,接下来会分析一个常用的类-java.lang.String 在 JVM 内部的信息。
JVM-漫游的更多相关文章
- 46张PPT讲述JVM体系结构、GC算法和调优
本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述.(内嵌iframe,建议使用电脑浏览) 好东西当然要分享,PPT已上传可供下载 ...
- java 利用ManagementFactory获取jvm,os的一些信息--转
原文地址:http://blog.csdn.net/dream_broken/article/details/49759043 想了解下某个Java项目的运行时jvm的情况,可以使用一些监控工具,比如 ...
- Jvm 内存浅析 及 GC个人学习总结
从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...
- JVM类加载
JVM的类加载机制就是:JVM把描述类的class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被JVM直接使用的Java类型 ClassLoader JVM中的ClassLoade ...
- Java虚拟机 JVM
finalize();(不建议使用,代价高,不确定性大) 如果你在一个类中覆写了finalize()方法, 那么你可以在第一次被GC的时候,挽救一个你想挽救的对象,让其不被回收,但只能挽救一次. GC ...
- 在 Linux 中安装 Oracle JDK 8 以及 JVM 的类加载机制
参考资料 该文中的内容来源于 Oracle 的官方文档 Java SE Tools Reference .Oracle 在 Java 方面的文档是非常完善的.对 Java 8 感兴趣的朋友,可以直接找 ...
- MapReduce剖析笔记之六:TaskTracker初始化任务并启动JVM过程
在上面一节我们分析了JobTracker调用JobQueueTaskScheduler进行任务分配,JobQueueTaskScheduler又调用JobInProgress按照一定顺序查找任务的流程 ...
- java太low,又舍不得jvm平台的丰富资源?试试kotlin吧(一)
尝试kotlin的起因 因为各种原因(版权,人员招聘),公司的技术体系从c#转到了java,我花了大概两周的时间来上手java,发现java的语法还是非常简单的,基本看着代码就知道什么意思.学习jav ...
- Jvm --- 常用工具
jps:虚拟机进程状况工具 JVM Process Status Tool. 可以列出所有目前正在运行虚拟机的进程. jps -l 详细参数: -q 输出LVMID,省略主类名称 -m 输出虚拟机进程 ...
- JVM虚拟机结构
JVM的主要结构如下图所示,图片引用自舒の随想日记. 方法区和堆由所有线程共享,其他区域都是线程私有的 程序计数器(Program Counter Register) 类似于PC寄存器,是一块较小的内 ...
随机推荐
- Android随笔之——Android单元测试
在实际开发中,开发android软件的过程需要不断地进行测试.所以掌握Android的单元测试是极其重要的.您应该把单元测试作为Android应用开发周期的一部分,精心编写的测试可以在开发早起帮你发现 ...
- IE6下的png透明图片的背景定位
在IE6下PNG透明图片做背景,无法使用background-position进行定位.但是可以使用margin和绝对定位来进行. 另外,由于IE6下的 :hover 只对<a>支持,对其 ...
- MySQL入门03-MySQL配置安全性、易用性
一.设定管理员用户和密码 二.处理test库权限隐患 三.自定义脚本提升易用性 中间定义文件 启动MySQL服务 关闭MySQL服务 快捷登录MySQL 四.设置开机自动启动MySQL服务 Refer ...
- JavaMail发送邮件的笔记及Demo
最近碰到一个需求,就是注册用户时候需要向用户发送激活邮箱,于是照着网上搜来的demo自己试着运行了一下,发件时我用的是网易163邮箱,收件时用QQ邮箱,运行后报了一个错误: 网络上搜索解决方式,多次尝 ...
- 一台电脑上的git同时使用两个github账户
需求: 公司有github账号,自己有github账号,想在git上同时使用,两者互不干扰. 思路: 管理两个SHH key. 解决方案: 一.生成两个SSH key 为了举例方便,这里使用“one” ...
- C++中的std详解
以下内容为:本人看C++视频教程-范磊主讲(2.91G)视频学习笔记. 与大家分享下,希望可以帮助大家学习c++! 引例: #include<iostream> int main() { ...
- what's this? 浅谈js中this的指向问题
刚刚学习js的朋友可能和我一样,看到代码中的this总是一脸懵逼,不知道this到底指向谁.经过一段时间的了解,我想跟大家分享下自己的理解. 何时出现this 函数在调用的时候,会自动获得两个特殊变量 ...
- springmvc原理
今天面试碰到一个特别恶心的公司面试官.是一个金融公司,过去后告诉我2点上班,带我去见经理.经理找人面试,看起来没有hr,经理直接看简历招人.经理上来就问我是xxx大学的,我说是,然后等面试官.面试官来 ...
- iOS通过ARC管理内存(内容根据iOS编程编写)
栈 当程序执行某个方法(或函数)时,会从内存中一个叫栈的区域分配一块内存空间,这块内存空间我们叫帧.帧负责保护程序在方法内声明的变量的值.在方法内声明的变量我们称之为局部变量. 当我们的程序开始启动, ...
- AssetsManagerEx 组件使用说明
原因 在网络上找了一圈也没有找到一个像样的说明.如果不是我们技术组的大大说这个东西可以用我都快放弃了. 稍微阅读了一下这个组件的源代码.发现该有的功能都有(如下所列). 其实最初吸引我们用这个东西的功 ...