深入理解JVM
文章目录
- 1. JVM内存区域划分
- 2. JVM中类加载过程
- 类加载
- (1)类加载的基本流程
- (2)双亲委派模型
《深入理解java虚拟机》
在这本书前,面试官对于JVM也不是很了解。
这本书主要还是写个一下开发 JVM 的人。
1. JVM内存区域划分
一个运行起来的Java进程,就是一个JVM虚拟机
需要从操作系统中申请一大块内存
就会把这个内存,划分成不同的区域,每块区域都有不同的作用
(1)方法区(1.7及其之前)/元数据区(1.8)
这里存储的内容,就是类对象(.class文件,加载之后,就成为了类对象)
(2)堆(占据空间最大的区域)
这里存储的内容就是代码中,new的对象
(3)栈
这里存储的内容,就是代码执行过程中,方法之间的调用关系
(4)程序计数器
比较小的空间,主要就是存放一个“地址”表示下一条要执行的指令,在内存中的哪个地方(方法区里)
每个方法里面的指令,都是以二进制的形式,保存在类对象中
虚拟机栈,程序计数器,每个线程都有一份
JVM进程中,有10个线程,就会有10个虚拟机栈和10个程序计数器
但是Heap(堆区)和Metaspace(元数据区),在JVM进程中只有一份(每个线程,有自己的程序计数器和栈空间;这些线程,公用一份堆和方法区)
每个线程有自己私有的栈空间
给你一个代码,问你某个变量,是处在内存中的哪个区?
- 在new Test()时,这个new 出来的 Test()在堆上,里面包含的成员变量也自然在堆上
- Test t 中的 t 变量是一个引用类型,是一个局部变量,处在栈上
- static int a 带有static修饰的是静态变量,也叫类属性,它包含在类对象中,处在方法区/元数据区
2. JVM中类加载过程
类加载
(1)类加载的基本流程
java代码会被编译成 .class 文件(包含了一些字节码),java程序要想运行起来,就需要让JVM读取到这些 .class文件,并且把里面的内容,构造成类对象,保持到内存的方法区中
书上和官方文档把这个类加载过程中,分为5个步骤:
- 加载:找到.class文件,打开文件,并且读取文件内容
(往往我们的代码中,会给某个类“全限定类名”)
例如 java.lang.String,java.util.ArrayList ,JVM就会根据这个类名,在一些指定目录范围内查找
- 验证:.class文件是一个二进制的格式(某个字节,都是有特定含义的)
验证格式是不是符合要求
- 准备:给类对象分配内存空间(最终的目的,是要构造一个类对象) 这里的分配内存空间,还没有初始化,此时这个空间上的内存的数值就是全0的
此时如果尝试打印类对象的static成员,就是全0的
- 解析:针对类对象中,包含的字符串常量进行处理,进行一些初始化操作
这个过程,也叫做:把“符号引用”(文件偏移量)替换成“直接引用”(内存地址)
- 初始化:针对类对象进行初始化
类中的各个属性设置好,初始化好static成员,还需要执行静态代码块,加载一下父类
(2)双亲委派模型
(查找优先级)
属于类加载中第一个加载步骤中的一个环节,负责根据 全限定类名,找到.class文件
类加载器,是JVM的一个模块
JVM中内置了三个类加载器:
1.BootStrap ClassLoader(爷)
2.Extension ClassLoader (父)
3.Application ClassLoader(子)
这个父子关系,不"继承"构成的,而是这几个ClassLoader 里有一个 parent 这样的属性,指向了一个父“类加载器”
双亲:parent 这个单词“双亲之一”
如果给定的类你是标准库的类,任务仍然会被交给孩子执行
6.没有扫描到,就会回到Extension ClassLoader
Extension ClassLoader 就会扫描负责的拓展库的目录,如果没有找到,还是把任务交给孩子执行;如果找到了,就执行后续的类加载操作,此时查找结束
7.如果没有扫描到,就会回到Application ClassLoader
Application ClassLoader 就会负责扫描当前项目和第三方库的目录,如果找到,就执行后续的类加载操作,如果没有找到,就会抛出一个ClassNotExcption
设计一套这样的流程,主要目的是为了确保,标准库的类,被加载的优先级最高,其次是拓展库,其次是自己写的类和第三方库
假设你在自己代码中,写了一个 java.lang.String ,实际JVM加载的时候,不会加载到你自己写的这个类,而是加载标准库的类