JVM是什么?
JVM是java虚拟机栈,用于运行java执行字节码文件的。是java实现跨平台的核心机制,因为它的目的是使用相同的字节码文件,在不同的操作系统运行的结果相同。
一、java内存模型
在JDK1.8之前,它是分为线程共享和线程私有的,在线程共享的部分分为堆区和方法区;在线程私有的部分分为jvm虚拟机栈、程序计数器、本地方法栈。在1.8之后,它是将方法区换为元空间。
jvm虚拟机栈:是由一个个的栈帧组成,每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。每一次方法调用结束后,都会有一个对应的栈帧被压入虚拟机栈,调用完后,代表该方法的栈帧会从虚拟机中弹出。
本地方法栈:native关键字修饰本地方法被执行时,在本地方法中也会创建一个栈帧,用于存放native本地方法的局部变量表、操作数栈、动态链接、方法出入口信息。
程序计数器:1、字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制;
2、在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候,能够知道当前线程的运行位置;
元空间:用于存放类信息、常量、静态变量、JIT即时编译器编译后的机器代码等数据。JDK1.6
时,方法区是一片连续的堆空间,经常出现OutOfMemory的异常;JDK1.7后将字符串常量池、静态变量转移到堆区;JDK1.8正式移除方法区,用元空间代替,并且是在本地内存。
堆: 又称GC堆;JVM管理的内存中最大的一块区域,被所有线程共享的一块内存区域。堆区中存放对象实例。堆区经常用分代划分,新生代和老年代。目的是更好的回收内存,更快的分配内存。
二、Java垃圾回收机制
分配策略:大对象、长期存活的对象优先进入老年代;而对象的创建优先zaiEden分配;
对象分配的过程:创建一个对象,判断它在Eden区是否可以放的下,如果放不下,则执行的是YGC,执行完YGC在判断这个对象是否能放的下,如果放不下,将它放入老生区,如果老生区放不下,那么执行FGC,如果执行完FGC还是放不下,则执行OOM(内存溢出)。
如何判断一个对象可以被GC回收:引用计数算法、根可达性算法
垃圾收集算法:标记-清除、标记-整理、标记-复制
垃圾收集器:
Serial收集器(新生代、标记-复制)
Serial Old收集器(老年代、标记-整理)
PerNew收集器(新生代 标记-复制,老年代 标记-整理)
Parallel Scavenge收集器(新生代,标记-复制)
Parallel Old收集器(老年代,标记-整理)
CMS收集器(老年代,标记-清除)初始标记、并发标记、重新标记、并发清除;(占用CPU,产生大量空间碎片)
G1收集器(老年代,局部性收集思想) 初始标记、并发标记、最终标记、筛选回收 (空间整合、可预测的停顿)
三、类加载机制
什么情况下需要类加载?
当一个类第一次被运行的时候(new关键字创建对象时、getstatic访问静态成员变量时、putstatic给静态成员赋值时、invokestatic调用类的静态方法时)
类的生命周期:
一、加载(通过类的完全限定名来获取这个类的二进制字节流;将该字节流表示的静态存储结构转换为元空间运行时的存储结构,在内存中生成一个代表该类的Class对象,作为元空间中该类各种数据的访问入口)
二、验证(Class字节流包含的信息是否符合当前虚拟机的要求)
三、准备(为类变量分配内存并设置初值)
四、解析(将字符串的符号替换为直接引用的过程)
五、初始化(初始化类变量和其他资源)
3.1 双亲委派模型
启动类加载器(java.util.* ,java.io.*, java.lang.*等基础类库)
扩展类加载器(是由javax扩展类库提供的)
应用程序类加载器
一个类加载器首先将类加载请求转发到父类,只有当父类无法完成时才尝试自己完成。
作用:使Java类随着它的类加载器一起具有一种带有优先级的层次关系,从而使基础类得到统一;实现热加载;
3.2 对象创建的过程
一、类加载检查;
二、内存分配
三、初始化零值;
四、设置对象头;
五、执行init()方法。