JVM是可运行Java代码的假想计算机,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法区,JVM运行在操作系统之上,与硬件没有直接交互
1、Java源文件 ——> 编译器 ——> 字节码文件(.class 二进制文件)
2、字节码文件 ——> JVM(类加载器) ——> 机器码
这也是为什么Java可以跨平台,一个程序启动,虚拟机就开始实例化,多个系统启动则有多个虚拟机实例。程序退出,虚拟机消亡,多个虚拟机实例间数据不能共享
JVM中的Java线程与原生操作系统线程有直接的映射关系,当线程本地存储、缓冲区分配、同步对象、栈、程序计数器等准备好后,就会创建一个操作系统原生线程。操作系统负责调度所有线程,并分配到任何可用CPU上,原生线程初始化完毕后,就会调用Java线程的run()方法,线程结束时释放原生线程和Java线程的所有资源。JVM后台运行的系统线程主要有以下几个:
本地方法栈与虚拟机栈类似,区别是虚拟机栈为执行Java方法服务,本地方法栈为Native方法服务
Java堆从GC的角度可以细分为新生代(Eden区、From Survivor区和To Survivor区)和老年代
用来存放新生的对象,一般占据堆的1/3空间,由于频繁创建对象,新生代会频繁触发MinorGC进行垃圾回收,又分为Eden区、From Survivor区和To Survivor区,只限于新生代的GC称为MinorGC
Java新对象的出生地(如果新创建的对象占用内存很大,直接分配到老年代),当Eden区内存不够时就会触发MinorGC,对新生代区进行一次GC
上一次GC的幸存者,作为这次GC的被扫描者
保留了一次MinorGC过程中的幸存者
MinorGC采用复制算法:
①eden、from复制到to,年龄+1
②清空eden、from
③to和from互换
【老年代每次只回收少量对象,因此采用标记-整理算法】
Java堆内存被划分为新生代和老年代两部分,新生代主要使用标记-清除和标记-复制垃圾回收算法,老年代主要使用标记-整理垃圾回收算法,因此JVM中针对新生代和老年代分别提供多种不同的垃圾收集器,HotSpot的垃圾收集器如下:
Serial和Serial Old分别用于新生代和老年代的垃圾回收,均是单线程的收集器。【单线程】不仅意味着它只会使用一个处理器或者一条收集线程来完成垃圾手机工作,更重要的是它进行垃圾回收时,【必须暂停其他所有工作线程】,直到收集结束
虽然Serial收集器需要暂停其他工作线程,但仍是HotSpot虚拟机运行在【客户端模式】下的默认新生代处理器,因为它简单而高效,对于限定单个CPU环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率
class SuperClass{
static{
System.out.println("Super init!");
}
public static int value=123;
}
class SubClass extends SuperClass{
static{
System.out.println("Sub init!");
}
}
public class NotInitialization{
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}
public class NotInitialization{
public static void main(String[] args) {
SuperClass[] superClasses = new SuperClass[10];
}
}
class ConstClass{
static {
System.out.println("ConstClass init!");
}
public static final String HELLOWORD="hello world!";
}
public class NotInitialization{
public static void main(String[] args) {
System.out.println(ConstClass.HELLOWORD);
}
}
【加载阶段,Java虚拟机需要完成三件事情:】
【关于加载过程中的类加载器在后文详细说明】
【准备阶段是正式为类中定义的变量(静态变量,被static修饰的变量)分配内存并设置变量初始值的过程,即在方法区中分配这些变量所使用的内存空间】
public static int value=123;
public static final int value=123;
【解析阶段是Java虚拟机将常量池内符号引用替换为直接引用的过程】,符号引用就是Class文件中的CONSTANT_Class_info、CONSTANT_Fieldref_info等类型的常量
【初始化阶段开始,虚拟机才真正开始执行类中编写的Java程序代码,将主导权移交给应用程序】
public class test2 {
static{
i=0;//编译通过
System.out.println(i);//编译不能通过 报错:Illegal forward reference
}
static int i=1;
}
public class test2 {
public static void main(String[] args) {
System.out.println(Sub.B);
}
}
class Parent{
public static int A=1;
static {
A=2;
}
}
class Sub extends Parent{
public static int B=A;
}
【Java虚拟机团队将加载动作放到JVM外部实现,以便让应用程序决定如何获取所需的类】