Snailclimb / JavaGuide
Code Issues 53 Pull requests 1 Actions Projects Wiki Security Insights
JavaGuide / docs / java / jvm / [
加
餐
]
大
白
话
带
你
认
识
JVM.md
rain7fine11 fix typo
4 contributors
master
来自掘金用户:说出你的愿望吧丷投稿,原文地址:
https://juejin.im/post/5e1505d0f265da5d5d744050#heading-28
前
言
如果在文中用词或者理解方面出现问题,欢迎指出。此文旨在提及而不深究,但会尽量
效率地把知识点都抛出来
一
、
JVM
的
基
本
介
绍
JVM 是 Java Virtual Machine 的缩写,它是一个虚构出来的计算机,一种规范。通过在
实际的计算机上仿真模拟各类计算机功能实现···
好,其实抛开这么专业的句子不说,就知道JVM其实就类似于一台小电脑运行在
windows或者linux这些操作系统环境下即可。它直接和操作系统进行交互,与硬件不直
接交互,而操作系统可以帮我们完成和硬件进行交互的工作。
1.1 Java
文
件
是
如
何
被
运
行
的
比如我们现在写了一个 HelloWorld.java 好了,那这个 HelloWorld.java 抛开所有东西不
谈,那是不是就类似于一个文本文件,只是这个文本文件它写的都是英文,而且有一定
的缩进而已。
那我们的 JVM 是不认识文本文件的,所以它需要一个
编
译
,让其成为一个它会读二进
制文件的 HelloWorld.class
①
类
加
载
器
如果 JVM 想要执行这个 .class 文件,我们需要将其装进一个
类
加
载
器
中,它就像一个
搬运工一样,会把所有的 .class 文件全部搬进JVM里面来。
②
方
法
区
方
法
区
是用于存放类似于元数据信息方面的数据的,比如类信息,常量,静态变量,编
译后代码···等
类加载器将 .class 文件搬过来就是先丢到这一块上
498 lines (296 sloc) 35.7 KB
③
堆
堆
主要放了一些存储的数据,比如对象实例,数组···等,它和方法区都同属于
线
程
共
享
区
域
。也就是说它们都是
线
程
不
安
全
的
④
栈
栈
这是我们的代码运行空间。我们编写的每一个方法都会放到
栈
里面运行。
我们会听说过 本地方法栈 或者 本地方法接口 这两个名词,不过我们基本不会涉及这两
块的内容,它俩底层是使用C来进行工作的,和Java没有太大的关系。
⑤
程
序
计
数
器
主要就是完成一个加载工作,类似于一个指针一样的,指向下一行我们需要执行的代
码。和栈一样,都是
线
程
独
享
的,就是说每一个线程都会有自己对应的一块区域而不会
存在并发和多线程的问题。
小
总
结
1. Java文件经过编译后变成 .class 字节码文件
2. 字节码文件通过类加载器被搬运到 JVM 虚拟机中
3. 虚拟机主要的5大块:方法区,堆都为线程共享区域,有线程安全问题,栈和本地方
法栈和计数器都是独享区域,不存在线程安全问题,而 JVM 的调优主要就是围绕
堆,栈两大块进行
1.2
简
单的代
码
例
子
一个简单的学生类
一个main方法
执行main方法的步骤如下:
1. 编译好 App.java 后得到 App.class 后,执行 App.class,系统会启动一个 JVM 进
程,从 classpath 路径中找到一个名为 App.class 的二进制文件,将 App 的类信息加
载到运行时数据区的方法区内,这个过程叫做 App 类的加载
2. JVM 找到 App 的主程序入口,执行main方法
3. 这个main中的第一条语句为 Student student = new Student("tellUrDream") ,就是
让 JVM 创建一个Student对象,但是这个时候方法区中是没有 Student 类的信息
的,所以 JVM 马上加载 Student 类,把 Student 类的信息放到方法区中
4. 加载完 Student 类后,JVM 在堆中为一个新的 Student 实例分配内存,然后调用构
造函数初始化 Student 实例,这个 Student 实例持有
指
向
方
法
区
中
的
Student
类
的
类
型信息
的引用
5. 执行student.sayName();时,JVM 根据 student 的引用找到 student 对象,然后根据
student 对象持有的引用定位到方法区中 student 类的类型信息的方法表,获得
sayName() 的字节码地址。
6. 执行sayName()
其实也不用管太多,只需要知道对象实例初始化时会去方法区中找类信息,完成后再到
栈那里去运行方法。找方法就在方法表中找。
二
、
类
加
载
器
的
介
绍
之前也提到了它是负责加载.class文件的,它们在文件开头会有特定的文件标示,将class
文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构,并且
ClassLoader只负责class文件的加载,而是否能够运行则由 Execution Engine 来决定
2.1
类
加
载
器
的
流
程
从类被加载到虚拟机内存中开始,到释放内存总共有7个步骤:加载,验证,准备,解
析,初始化,使用,卸载。其中
验
证
,
准
备
,
解
析
三
个
部
分
统
称
为
连
接
2.1.1
加
载
1. 将class文件加载到内存
2. 将静态数据结构转化成方法区中运行时的数据结构
3. 在堆中生成一个代表这个类的 java.lang.Class对象作为数据访问的入口
2.1.2
链
接
1. 验证:确保加载的类符合 JVM 规范和安全,保证被校验类的方法在运行时不会做出
危害虚拟机的事件,其实就是一个安全检查
2. 准备:为static变量在方法区中分配内存空间,设置变量的初始值,例如 static int a
= 3 (注意:准备阶段只设置类中的静态变量(方法区中),不包括实例变量(堆
内存中),实例变量是对象初始化时赋值的)
3. 解析:虚拟机将常量池内的符号引用替换为直接引用的过程(符号引用比如我现在
import java.util.ArrayList这就算符号引用,直接引用就是指针或者对象地址,注意引
用对象一定是在内存进行)
2.1.3
初
始
化
初始化其实就是执行类构造器方法的 <clinit>() 的过程,而且要保证执行前父类的
<clinit>() 方法执行完毕。这个方法由编译器收集,顺序执行所有类变量(static修饰的
成员变量)显式初始化和静态代码块中语句。此时准备阶段时的那个 static int a 由
默认初始化的0变成了显式初始化的3. 由于执行顺序缘故,初始化阶段类变量如果在静态
代码块中又进行了更改,会覆盖类变量的显式初始化,最终值会为静态代码块中的赋
值。
注意:字节码文件中初始化方法有两种,非静态资源初始化的 <init> 和静态资源
初始化的 <clinit> ,类构造器方法 <clinit>() 不同于类的构造器,这些方法都是
字节码文件中只能给JVM识别的特殊方法。
2.1.4
卸
载
GC将无用对象从内存中卸载
评论0