编程语言的层级关系
电脑硬件–>windows系统(C/C++)–>JVM虚拟机(C/C++)–>Java代码
JDK是工具包 ,JDK中包含jre,jre 就是JVM 虚拟机
JVM内部结构
- 绿色是功能区域(真正干活的),功能(类加载,垃圾回收,执行引擎) 类加载是入口 ,执行引擎是出口。
- 橙色是线程共享区域,每个线程进来共享的区域(包括方法区、Java堆、直接内存(java1.4非阻塞NIO之后就没了)大小受电脑内存影响),类存放在方法区,相当于模板 共享,根据模板创建类的实例对象 ,存在栈中。)
- 蓝色是线程私有区域,每个线程进来都会开辟一个栈,当线程执行完了 ,栈、本地方法栈、pc寄存器就会销毁。没法共有,直接销毁。
public class Demo1(){ //Demo类属于模板 存在方法区,对象是类的实例
public void demo1(String i){ //成员变量demo1是方法 存在栈中,形参i 是局部变量存在栈中
Demo1 demo1 = new Demo1();
//Demo1为类 方法区
//demo1 引用(指向 Java堆中对象的 引用地址) 栈中
//new Demo1() 类的实例 java堆中
int i = 1 ; //局部变量 栈中
}
}
执行流程
类加载器
- Bootstrap Class Loader C++写,JVM启动, 加载Java基础类,对应的文件是%JRE_HOME/lib/
目录下的rt.jar、resources.jar、charsets.jar和class等 - Extension Class Loader 继承URL ClassLoader 加载扩展类 对应的文件是 %JRE_HOME/lib/ext 目录下的jar和class等
- App Class Loader 继承URL Class Loader ,系统类加载器
对应的文件是应用程序classpath目录下的所有jar和class等
public class Demo2(){
public static void main(String[] args){
System.out.println(HKSCS.class.getClassLoader());//bootstrap Class Loader
System.out.println(ZipInfo.class.getClassLoader());//Extension Class Loader
System.out.println(Demo2.class.getClassLoader());//App Class Loader
}
}
面试题:java双亲委派机制及作用 (类加载机制)
- 什么是双亲委派机制
当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类 - 双亲委派机制的作用
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。防止把jdk中的类覆盖。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
执行引擎
作用 : 执行编译后的字节码指令.
主要的执行技术有 : 解释执行,即时编译执行,自适应优化执行, 芯片级直接执行
(1)解释:属于第一代JVM;
(2)即时编译:JIT属于第二代JVM;
(3)自适应优化:(目前Sun的HotspotJVM采用这种技术)吸取第一代JVM和第二代JVM的经验,采用两者结合的方式。
开始对所有的代码都采取解释执行的方式,
并监视代码执行情况,然后对那些 经常 调用的方法启动一个后台线程,将其编译为本地代码,并进行仔细优化。
若方法不再频繁使用,则取消编译过的代码,仍对其进行解释执行;
(4)芯片级直接执行:内嵌在芯片上,用本地方法执行Java字节码。
Java栈 、本地方法栈J
两者合并为栈,主要存放动态链接、局部变量等信息,线程创建的时候,就创建栈
因为只有一个进口 和出口 ,栈中的方法是现进后出,
方法出口是指 每执行完一个方法之后要回到 调用此方法的前一个方法中,理解很抽象。
本地方法栈作用 : 用于本地方法调用, JDK源码中好多使用了Native关键字, 也就是调用底层C语言编写的方法.,这种方法是实现类的方法,但是没有方法体。合并之后存在栈中
栈中放置以下内容
栈中存放基本类型的原值和引用类型的地址值.
基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress 引用类型包括:类类型,接口类型和数组,方法的实例
PC寄存器
作用 : 每个线程启动的时候,都会创建一个PC寄存器。保存下一条将要执行的指令地址. PC寄存器的内容总是指向下一条将被执行指令的地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。
以下为线程共享区
方法区(元空间)
jdk1.8之后 就改名为元空间 ,不再占JVM的内存(1/4内存条)了,直接占内存条(16G)的内存。所以1.8之后就不会溢出
作用 : Java方法区和堆一样,方法区是一块所有线程共享的内存区域,他保存系统的类信息。比如类的字段、方法名(方法的模板,不是真实的方法)、常量池等。
方法区的大小决定系统可以保存多少个类。如果系统定义太多的类,导致方法区溢出。虚拟机同样会抛出内存溢出的错误。
Java堆
堆,栈,方法区之间的关系图:
堆的内部结构(垃圾回收 复制算法)
直接内存
作用 : 提高一些场景中的性能.
直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中农定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
本机直接内存的分配不会受到Java 堆大小的限制,受到本机总内存大小限制
配置虚拟机参数时,不要忽略直接内存 防止出现OutOfMemoryError异常
直接内存(堆外内存)与堆内存比较
直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显 直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显
未完待续……