Java虚拟机(JVM)
概述
JVM是一种计算设备的规范
JVM是一种虚构出来的计算机,在实际计算中仿真模拟计算机功能来实现
JVM屏蔽了与具体操作系统平台相关的信息,使程序只生成在JVM上运行的字节码(多平台复用)
JVM是JRE的一部分,具有完善的硬件架构(处理器、堆栈、寄存器等),还有相应的指令系统
-------------------------------------------------------------------------------------------------------------------------------------------------------------
原理
执行过程:1、加载. class文件 2、分配内存 3、执行GC
生命周期:1、启动:启动一个Java程序时就实例化JVM,任何一个有public static void main(String[] args)方法的类都可以作为JVM实例运行的起点
2、运行:main()是程序初始线程的起点,任何其他线程都是由它启动。JVM内部有2种线程:守护线程和非守护线程。main()是非守护线程,守护线程由JVM自己使用,Java程序中也可以设置创建的线程是守护的。
3、消亡:所有非守护线程都终止时,JVM退出,程序也可以使用Runtime类或System.exit()来退出。
-------------------------------------------------------------------------------------------------------------------------------------------------------------
体系结构
注:类加载器(ClassLoader)用来装载.class文件
执行引擎用来执行字节码和本地方法
运行时数据区(方法区、堆、栈、寄存器、本地方法栈)↓
-------------------------------------------------------------------------------------------------------------------------------------------------------------运行时数据区
JVM规定了程序运行时使用到的 数据区,即内存区域划分模型
数据区图↓
注:线程共享是指有些区域是随着JVM的启动和退出而创建和销毁的,线程私有是指有些区域是有各自的线程,不共享。
堆:提供线程共享的运行时内存区域,还是类实例和数组对象分配内存的区域。
作用:存放对象和实例。
特点:堆的大小可以是固定的也可以动态扩展,且不必要是连续的。
方法区:是堆的逻辑组成部分。
作用:存放已被JVM加载的类的结构信息(属性、方法、接口、构造函数等)、常量、静态变量。
注:还包含运行时常量池(Java1.7后移到堆中),存放编译时产生的字面量和符号引用,但是常量不一定要在编译时就产生,也可以是在运行时产生新的常量放进去,如String的intern()方法。
程序计数寄存器:每个线程独立拥有,理解为当前线程所执行的字节码的行号指示器。
注:JVM规范中唯一一个没有规定OutOfMemoryError的区域
作用:1、线程执行Java方法时,存放正在执行的JVM字节码指令的地址,2、线程执行native方法,存放underfined
虚拟机栈:每个方法在执行的时候都会创建一个栈帧用于存储局部变量表、操作数栈、方法出口等信息,每一个方法从调用到执行完成就是一个栈帧入栈和出栈的过程。
注:局部变量表存放编译时可知的各种基本数据类型、对象引用和指向了一条字节码指令的地址。
本地方法栈:和虚拟机栈类似,存储native方法的相关信息。
-------------------------------------------------------------------------------------------------------------------------------------------------------------
JVM垃圾回收(GC)
原理:将内存中不再被使用的对象进行回收,GC中用于回收的方法称为收集器。由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停。
1、对新生代的对象的收集称为minor GC
2、对旧生代的对象的收集称为Full GC
3、程序中主动调用System.gc()强制执行的GC为Full GC
不同的对象引用类型, GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:
1、强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)
2、软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)
3、弱引用:在GC时一定会被GC回收
4、虚引用:由于虚引用只是用来得知对象是否被GC
-------------------------------------------------------------------------------------------------------------------------------------------------------------
引申
今天在看一篇文章的时间,提及Java是不是纯粹的面向对象的语言,提出了一些疑问。
比如
1、所有静态变量和方法(static关键字修饰)
2、基本数据类型(short、int、long、char、boolean、byte、float、double) 都不是对象。
但是笔者提出了一个他的发现↓
1、
JVM在创建对象的时候实际上是创建了2个对象:
---实例对象
---class对象,该对象仅仅被装载一次,同时该类的静态内容也一起被装载,JVM使用该class对象来创建具体的实例对象。
如:
Student s = new Student();
s是实例对象,另一个是class对象,可以通过Student.class引用到。若通过s对象来访问静态内容的话,本质上是指向的对象是Student.class。
注意:这就是为什么静态内容在任一个实例对象中(s,s1,s2....)中改变了,在另一个对象中也同时改变了。
综上,第一条疑问被破解了,静态内容也是对象!
2、
基本数据类型不能作为对象的原因是它们不能像对象一样进行类似使用‘.’来访问属性方法的操作。
其实,Java官方为了解决这个问题提出了对应的包装类的概念。
我们能做的是 为基本数据类型创建一个包装对象,对包装类做对象相关的操作。由于是自动装拆箱,可以将其值赋给对应包装类的引用。
如:
Integer obj = new Integer(3); //可以做obj.toString();操作
int i = 4; //不能做对象相关操作
换个角度看待,作为开发人员,我们是Java的使用者不是创造者,而JVM是创造者,它将基本数据类型看做是对象处理着,详情在Class类的源码中可以发现,在JVM内部基本数据类型就是被看做是对象。(可以查看primitive)
那么,既然JVM为每个基本数据类型创建了一个对象,那为什么还是喜欢用基本数据类型而不是它对应的包装类对象呢?
因为:JVM创建的对象是轻量级的(内部优化了,很多方法没了),这样导致功能少了很多,并没有基本数据类型好用了。
综上,在JVM中确实将基本数据类型当作对象的,我们开发人员要对其对应的包装类来操作。
--------------------------------------------待完善-------------------------------------------