JAVA进阶--JVM
一.JVM的概述
java语言有跨平台特点, 写一次java程序,可以在不同的平台上运行.(JVM虚拟机的作用)
前提条件: 在不同的平台上安装不同的虚拟机(虚拟机就是一个翻译)
.java--->.class--->不同的虚拟机--->机器码
1.jvm作用:
负责将字节码翻译为机器码, 管理运行时内存
2.jvm的整体组成部分
类加载系统: 负责将硬盘上的字节码文件加载到内存中(重点)
运行时数据区: 负责存储运行时各种数据(重点)
执行引擎: 负责将字节码 转为机器码
本地方法接口: 负责调用本地方法(非java的方法)
垃圾回收(重点)
二.类加载系统
1.作用
负责将硬盘上的字节码文件加载到内存中(运行时数据区中)
2.类什么时候会被加载
(1).在一个类写一个main方法,运行main方法
(2).new 某个类对象时
(3).使用类中的静态成员
(4).使用反射机制时
public class Hello {final static int num = 10;static int num1= 10;/*静态代码块在类被加载时自动执行,目前可以看做一个类只被加载一次*/static {System.out.println("类被加载了");}public static void main(String[] args) {System.out.println("111111111");}
}public class TestHello {public static void main(String[] args) throws ClassNotFoundException {//new Hello();//System.out.println(Hello.num);// Class.forName("com.ffyc.javapro.jvm.classloader.Hello");/*创建的是数组对象,数组是Hello类型*/Hello [] hellos = new Hello[10];//只是访问类中的静态常量,类是不加载的,直接返回静态常量值即可System.out.println(Hello.num);//System.out.println(Hello.num1);}
}
3.类加载的过程 (了解)
加载: 以字节流形式读取文件
链接: 验证 准备 解析
初始化: 主要为静态成员变量初始化赋值
4.类加载器
类加载器就是负责加载类的实践者
不同的类,是否不同的类加载器加载的
类加载器分类:
启动类加载器(引导类加载器),不是用java语言写的,是c/c++写的, 负责加载虚拟机核心的类库
扩展类加载器 ,是用java语言写的,负则加载jre/lib/ext目录下的类
应用程序类加载器,是用java语言写的,负责加载程序员写的项目中的类(target/class)
public static void main(String[] args) {//获得类的类加载器
System.out.println(String.class.getClassLoader());//类加载器为null,说明是由启动类加载器加载的String类System.out.println(Hello.class.getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(Hello.class.getClassLoader().getClass().getClassLoader());//应用程序类加载器的加载器为null}
5.什么是双亲委派机制(重点)
工作原理:
当收到类加载任务时,首先委派给上级的类加载器加载,如果上级类加载器还有父级,依次递归,直到最顶级的启动类加载器, 当父级类加载器找到类时,成功返回,
如果找不到,就委派给子类加载器,如果子类加载器找到后,成功返回,
如果均未找到,那么就抛出ClassNotFoundException.
7.为什么设计双亲委派机制
为了安全, 避免了自己定义的类,替换了系统中的核心类
例如: 自己创建java.lang.String, 结果还是加载的系统中的String类
8.如何打破双亲委派机制
自定义类加载重写方法打破双亲委派机制
①可以自定义类加载器
②写一个类 继承 ClassLoader类,
③重写findClass();
自己用流将字节码读入,
Class<?> clazz = defineClass(null, bytes, 0, bytes.length);
Object o = clazz.newInstance();//反射机制创建对象//com.ffyc.javapro.jvm.classloader.MyClassLoader@1b6d3586
System.out.println(clazz.getClassLoader());
三.运行时数据区
当类加载系统把类信息加载到内存后,存储到运行时数据区.
运行时数据区,根据不同的功能可以分为5个部分:
1.程序计数器
作用:
程序计数器用来记录线程执行的指令集的位置, 因为线程在执行时cpu要进行切换执行,需 要记录线程执行的位置.
特点:
(1).是运行时数据区中空间最小的,运行速度最快的区域
(2).每个线程都有一个属于自己的程序计数器,是线程私有的,程序计数器生命周期与线程生命 周期相同.
(3).程序计数器是运行时数据区中唯一一个不会有内存异常情况的区域,
2.虚拟机栈
虚拟栈是运行单位,管理程序如何执行,调用一个方法,方法入栈执行,运行结束后,出栈.
虚拟机栈主要用来运行java语言写的方法.
特点:
①线程私有的,每个线程中调用的方法都在线程对应的虚拟机栈中执行.
②栈中存储局部变量
③虚拟栈中不存在垃圾回收
虚拟机栈中会存在内存溢出问题(递归调用太深)
Exception in thread "main" java.lang.StackOverflowError 栈溢出错误
④不同线程中所包含的栈帧(方法)是不允许存在相互引用的
(eg:A线程中的方法不能调用B线程中的方法)
⑤先进后出
public void test(){ int a = 10;//局部变量int b= 20;String s = new String(); //s是引用类型,保存的是对象地址
}
栈帧:
当一个方法被调用后,被压入到虚拟机栈中称为一个栈帧,
栈帧内部结构:
①局部变量表(存储局部变量的区域)
②操作数栈 —— 操作数栈就是用来计算的区域,
例如 :
int a= 10,int b=20; //a和b存储在局部变量表中
int c = a+b ;//计算时,把a和b从局部变量表加载到操作数栈运算,把 运算结果赋给c,把c写回到局部变量表
③方法返回地址: 记录方法调用的位置,方法执行完成后要回到自己开的位置
3.本地方法栈
本地方法: 在java程序中,不是用java语言实现的方法, 由底层操作系统提供
使用 native关键修饰的方法,没有方法体
因为java语言属于上层语言(开发上层应用程序),没有权限与底层硬件进行交互(如读取内存数据,读取硬盘数据)
本地方法栈用来执行本地方法的,当程序中调用了本地方法,那么被加载到本地方法栈中运行.
特点:
①线程私有的,每个线程都有属于自己的本地方法栈
②本地方法栈也会出现内存溢出情况
③本地方法栈中不会出现垃圾回收
4.堆
(1).概述
作用:
堆空间是用来存储java中创建的对象的
特点:
堆空间是运行时数据区中最大的一块内存空间,
还可以根据需要通过参数设置大小: -Xms:10m(堆起始大小) -Xmx:30m(堆最大内大小
堆空间是所有线程共享的.
堆空间会出现内存溢出情况的.
堆空间是垃圾回收的重点区域.
(2).堆内存区域划分
新生代(区): 伊甸园区 幸存者0(from) 幸存者1(to)
老年代(区):
(3).为什么要分区(代)
根据对象的存活周期,对象的大小放在不同的区域,不同的区域可以采用不同的垃圾回收算法.会频繁的回收新生代, 相对较少回收老年代.可以对回收算法扬长避短.
(4).对象创建内存分配过程
①.新创建的对象都存储在伊甸园区(比较大的对象,可以直接分配到老年代)
②.当下次垃圾回收到来时,把伊甸园区存活的对象移动到幸存者0区,清空伊甸园区
③.当下次垃圾回收时,把伊甸园区中存活的对象和幸存者0区的存活对象移动到幸存者1区,清空伊甸园区和幸存者0区.
④.当一个对象经历过最大上限15次垃圾回收后,依然存活,那么将此对象移动到老年代
5.方法区
(1).概述
方法区主要存放类信息(属性,方法,静态常量...)和编译器编译后的代码
是一个被线程共享的内存区域
方法的大小也是可以设置的,方法区的大小决定可以加载多少了类
方法区也是有可能出现内存溢出的.
(2).方法区大小设置
可以使用参数-XX:MetaspaceSize指定
(3).方法区的垃圾回收
方法区也是有垃圾回收的, 方法区的垃圾回收主要回收的是类信息.
类信息回收条件(比较苛刻的):
①.该类所创建的对象都已经不再使用,并且被回收了
②.该类的Class对象也不在被使用了
③.加载该类的类加载器也被回收了
线程共享: 堆,方法
线程私有的: 程序计数器,虚拟机栈,本地方法栈
会出现内存溢出: 堆,方法, 虚拟机栈,本地方法栈
会出现垃圾回收: 堆,方法区
四.本地方法接口 (了解)
虚拟机中负责调用本地方法的入口, 本地方法运行在本地方法栈中.
什么是本地方法:
被native修饰的方法, 没有方法体, 是操作系统提供的方法
为什么java中要调用本地方法:
java属于上层应用开发语言,没有权限直接访问计算机硬件(硬盘,内存,外设(喇叭)),需要调用本地操作系统提供的方法.
五.执行引擎(黑盒) (了解)
执行引擎在虚拟机中主要负责将加载到虚拟机中的字节码 解释/编译 为机器码
.java-----jdk编译--->.class 在开发阶段 (前端编译)
.class---执行引擎编译---->机器码 在运行阶段(后端编译)
什么是解释器?什么是 JIT 编译器?
解释器/解释执行--->sql,html,css,js ,python 解释执行 不需要整体编译,由解释器一行一行执行
JIT编译器-->编译执行,先把代码整体进行编译,生成另一种文件格式,但并不是马上执行
解释执行特点:
速度慢, 不需要花费时间编译
编译执行特点:
编译后执行快, 但是编译需要花费一定的时间
jvm中的执行引擎在将字节码 翻译为机器码时,采取半解释,半编译机制.
开始时,可以先采用解释执行,立即投入到翻译工作中,
等到编译器编译完成后,采用编译执行