Java--JVM--体系结构概论
- JVM的位置
-
JVM体系结构概览
- 灰色的 java栈(java stack),本地方法栈(Native Method Stack),程序计数器(Program Counter Register)是线程私有的并且空间非常小
-
类装载器(Class Loader)
-
抽象类不能进行实例化,只能继承
- 加载器的种类
-
启动类加载器(Bootstrap)
-
扩展类加载器(Extension)
-
应用程序类加载器(AppClassLoader)
-
用户自定义的加载器
-
-
双亲委派机制(保证了沙箱安全)
-
我们写一个和java源代码一样的类(java.long.String)
-
运行这个类时,虚拟机会先在启动类加载器中找,有没有这个类,有就用这个类,没有在往下走在扩展类加载器中找有没有这个类,找到了就用就不会在往下走了
-
如果两个都没有找到这个类,才会进入到应用程序类加载器中找,先找到先使用,后面的一改不计较
-
保证了如果我们写了和java源码一样的类时,不会污染源码的类,保证java的源码是同一个
-
-
类加载器的问题总结
-
什么是类加载器
-
负责加载class文件,class文件在 文件开头有特定的文件标示,将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的
-
运行时数据结构并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定
-
-
有几种
- 默认的有三种
- 启动类加载器(Bootstrap)
- 扩展类加载器(Extension)
- 应用程序类加载器(AppClassLoader)
- 用户自定义的
- 默认的有三种
- 类加载器的优势
- 避免了类的重复加载
- 保护了程序的安全,避免了API被随意篡改
-
什么 是双亲委派机制
-
当一个类加载器收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个
请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求
都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求
的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试
自己去加载。 -
采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object, 不管
是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这
样就保证了使用不同的类加载器最终得到的都是同样--个Object对象。
-
-
沙箱安全
- 我们自定义的和java源码的类一样时,由于相亲委派机制的存在,会加载java自带的类,而造成类的重复加载,保证了对java源代码的保护,防止我们的代码污染源代码,实现了沙箱安全
-
将java代码限定在虚拟机运行范围内,严格限制代码对本地系统资源的访问,保证对代码的有效隔离,防止对本地系统造成破坏
-
-
-
本地方法接口(Native Method Interface)
-
同一个线程不能同时开启两次,会爆出 线程状态不合法的错误
-
package com.model.jvm; public class Thread01 { public static void main(String[] args) { //java.lang.IllegalThreadStateException , 不合法线程的状态异常 Thread thread=new Thread(); thread.start(); thread.start(); } }
-
-
方法中表示 native 的表示:Java自己搞不定,会请求外援,调用的底层C语言库和操作系统库(1995.10,java诞生)
- native修饰的方法(特殊的方法),会放在native栈中(特殊的栈),由native表示的方法是本地方法接口(native interface) 会放在本地方法栈中(native method stack)
- 本地接口(Native Interface)
-
-
PC寄存器(Program Counter Register)
-
每个线程都有一个程序计数器,是线程私有的,就是一个指针,
指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将
要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内
存空间,几乎可以忽略不记。 -
这块内存区域很小,它是当前线程所执行的字节码的行号指示器,字节
码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令。 -
如果执行的是一个Native方法,那这个计数器是空的。
-
用以完成分支、循环、跳转、异常处理、线程恢复等基础功能。不会发
生内存溢出(OutOfMemory=0OM)错误
-
- 方法区(Method Area)(堆,方法区:所有的线程共享,存在垃圾回收)
-
供各线程共享的运行时内存区域。它存储了每一个类的结构信息(类的模板),例如运行时常量池(Runtime Constant Pool) 、字段和方法数据、构造函数和普通方法的字节码内容。上面讲的是规范,在不同虚拟机里头实现是不一样的,最典型的就是永久代(PermGen space)和元空间(Metaspace)。
-
空调 kI = new 格力()
List list = new ArrayList();
方法区 f =new 永久代
方法区 f =new 元空间 -
实例变量存在堆内存中,和方法区无关
- 存放:类的结构信息,静态方法,常量,静态变量
-
- java 栈(java stock)
- 堆(heap)
- native详解:
-
native标识的方法可以实现:在类中只有方法的声明,没有方法的实现
-
调用 c语言的底层函数库
-
小总结
-
JVM系统架构图
- 类加载器
-
//null 启动类加载器由于时c++编写所以是null (Bootstrap) System.out.println(test01.getClass().getClassLoader().getParent().getParent()); //sun.misc.Launcher$ExtClassLoader@14ae5a5 扩展类加载器(Extension) System.out.println(test01.getClass().getClassLoader().getParent()); //sun.misc.Launcher$AppClassLoader@18b4aac2 应用程序加载器(AppClassLoader) System.out.println(test01.getClass().getClassLoader());
- 相亲委派机制
- 沙箱安全机制
-
- native 关键字
- 本地方法,不归java管,是底层的C语言调用函数库
- native的方法放在本地方法栈中
- native的方法由方法的声明,但是没有方法的实现,是因为这里就不是用java操作范围了,会调用其他的c语言底层函数库
- 程序计数器(pc寄存器)
- 记录了方法之间的调用和执行情况,类似于排版值日表
- 用来存储指向下一条指令的地址,也即将要执行的指令代码,他是当前线程所执行行的字节码的行号指示器
- 指针行号指示器、
- 方法区(Method Area)
- 供各线程共享的运行时内存区域。它存储了每一个类的结构信息(类的模板),例如运行时常量池(Runtime Constant Pool) 、字段和方法数据、构造函数和普通方法的字节码内容。上面讲的是规范,在不同虚拟机里头实现是不一样的,最典型的就是永久代(PermGen space)和元空间(Metaspace)。
- 空调 kI = new 格力()
List list = new ArrayList();
方法区 f =new 永久代
方法区 f =new 元空间
-