JVM

JVM

简书首页 (小名源治)

1. JVM体系结构




JVM体系结构




堆和栈的关系

  • “对象的引用”在栈里面
  • 真正对象的“数据”在堆里面

2.类加载器及双亲委派机制

类加载器

作用:加载class文件

类是模板,对象是具体的

public class Test {
public static void main(String[] args) {
//类是模板,对象是具体的
Class&LTTest> testClass = Test.class;
}
}

test1,test2,test3 三个对象都是类模板new出来的,通过第一次输出可以知道,三个对象是不一样的。
但是他们getClass后输出的结果就是一样的,因为getClass得到的是类的模板





类和对象





image.png

加载器的分类
  • 虚拟机自带的加载器
  • 启动类(根)加载器 BOOT
  • 扩展类加载器 EXC
  • 应用程序(系统类)加载器 APP

双亲委派机制

APP ---> EXC ---> BOOT
BOOT中不存在 ---> EXC中不存在 ---> APP(当前应用)
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。

  • 1、当 AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
  • 2、当 ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader```去完成。
  • 3、如果 BootStrapClassLoader加载失败(例如在 $JAVA_HOME/jre/lib里未查找到该class),会使用 ExtClassLoader来尝试加载;
  • 4、若ExtClassLoader也加载失败,则会使用 AppClassLoader来加载,如果 AppClassLoader也加载失败,则会报出异常 ClassNotFoundException。

举个例子

我们模拟Java中lang包下的String类,它一直会询问自己的父加载器有没有这个类,恰好BOOT中有这个类,但是其中没有main方法,就会报错。
假如是换一个名字,根加载器中没有这个Student类,那么它就能正常加载成功。





image.png




image.png

3、native关键字

凡是被native修饰的,说明java的作用范围达不到了,会去调用c,c++语言的库!

以“start0();”方法为例

  • 调用被native关键字修饰的方法,会进入本地方法栈
  • 然后会调用本地方法的本地方法接口(JNI)
  • JNI的作用:扩展java的使用,融合不同的编程语言为java所用!最初是c/c++
  • java诞生的使用,c/c++很流行,想要立足,就必须要用c/c++的程序
  • 因此,它在内存区域中专门开辟了一块标记区域Native Method Stack,来登记native方法
  • 在最终执行的时候,通过JNI(本地方法接口)来加载本地方法库中的方法
  • 平常用不到这类的方法,该方法多操控硬件资源

4、PC寄存器

程序计数器:Program Counter Register(PC计数器或者指令计数器更贴切):
-每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计

如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)

5、方法区

方法区 Method Area

  • 又被称为静态区,它跟堆一样被所有线程共享
  • 它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据
  • 方法区中包含的都是整个程序中永远唯一的元素,如:class、static变量。

6、栈

栈(先进后出):也叫栈内存,主管程序的运行,它的生命周期和线程同步;线程结束,栈内存也就释放了;对于栈来说,不存在垃圾回收问题;一旦线程结束,栈就“Over”;

  • 每个线程都包含一个栈区,栈中只保存基本数据类型的值和对象以及基础数据的引用。(8大基本类型+对象引用+实例的方法)
  • 每个栈中的数据(基本数据类型和对象的引用)都是私有的,其它栈是无法进行访问的。
  • 栈分为三个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

栈运行原理:栈帧,程序正在执行的方法一定在栈的顶部
什么是栈溢出:(StackOverflowError)

package com.my.test;
//理解什么叫栈溢出
//为什么main()方法先执行后结束
public class Test03 {
public static void main(String[] args) {
Test03 test03 = new Test03();

test03.a();
}
public void a(){
b();
};
public void b(){
a();
};
}

7、三种JVM

  • sun公司的HotSpot(Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode))
  • Oracle公司的 JRockit
  • IBM J9 VM

8、堆(Heap)

Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。
Java堆是垃圾收集器管理的内存区域,因此它也被称作“GC堆”。
一个JVM只有一个堆内存,堆内存的大小是可以调节的。
堆内存中细分为三个领域:

  • 新生区(伊甸区)young/new
  • 养老区old
  • 永久区perm



    image.png

GC垃圾回收:主要在伊甸园区和养老区

“OOM”:内存满了,堆内存不够

JDK8以后永久存储区更名为“元空间”

新生区

  • 类:诞生和成长以及死亡的地方
  • 伊甸园区:所有的对象都是由伊甸园区new出来的

永久区

  • 概念:
    这个区域常驻内存,用来存放JDK自身携带的Class对象、interface元数据、存储的是java运行时的一些环境或者类信息
    这个区域不存在垃圾回收;除非关闭虚拟机才会释放这个区域的内存

Report Page